|
|
|
|
OCMock is an Objective-C implementation of mock objects. If you are unfamiliar with the concept of mock objects please visit mockobjects.com which has more details and discussions about this approach to testing software. This implementation fully utilises the dynamic nature of Objective-C. It creates mock objects on the fly and uses the trampoline pattern so that you can define expectations and stubs using the same syntax that you use to call methods. No strings, no @selector, just method invocations like this: [[myMockObject expect] doSomethingWithObject:someObject];
Pre-built binaries as well as instructions on how to access the source code are available on the "Download" tab. The "Features" tab lists all features supported by OCMock. Support details, including a link to the support forum, are on the "Support" tab. 2-Mar-2012New binary release (2.0) bringing compatibility with iOS 5 and Xcode 4.2/4.3. The version is 2.0 not because of a major change in OCMock itself but because the 1.x numbering scheme depended on Subversion, and the code is now on Github. 20-Aug-2011Some infrastructure changes. Following public demand the source code for OCMock is now on GitHub (erikdoe/ocmock). Going forward this will be the master repository for the source code. Because the current version numbers are based on the Subversion revisions we will introduce a new numbering scheme with the next release. At the same time the website is moving to its own top-level domain, ocmock.org. 17-Mar-2011New release (1.77) bringing Xcode 4 compatibility, an explicit check to see whether the static library has been linked correctly, and a feature to stop a partial mock from intercepting calls to the underlying object. 21-Aug-2010New release (1.70) which adds the following features: support for blocks, static library for iOS development, forwarding of methods from a partial mock to the real object, and rejecting methods when using nice mocks. All new features are highlighted on the "Features" tab on this page, and the static library is described in detail on the "iPhone/iOS" tab. 16-Oct-2009New release (1.55) offering several new features, including partial mocks, method swizzling, posting of notifications, and verification of call sequence, as well as a handful of bug fixes. Details in the "Features" tab on this page and in the change notes. 10-Oct-2009The mailing list is great for submitting patches and discussing future development of OCMock. To just get a quick answer for a problem using OCMock it might be a bit too involved, which is why we're going to trial the use of a web-based forum. Have a look at the brand new OCMock Forum. 19-May-2009New release (1.42) which combines several contributions, adding support for mock observers of notifications, setters for pass-by-reference arguments, and several improvements to existing features. The default binary release now uses @rpath, which allows for more flexible deployment, and it supports garbage collection. 07-Jul-2008With this new release (1.29) OCMock supports hamcrest matchers like this: [[mock expect] doSomething:startsWith(@"foo")]
Note that this dependency is optional. OCMock does not require or link against hamcrest, but if the test suite uses hamcrest matchers and links against hamcrest then OCMock works with the matchers. This release also contains a small bugfix that removes a memory leak in OCMockRecorder. 08-May-2008New release (1.24) which combines several contributions, adds support for more flexible constraints as well as experimental 64-bit support. The default binary release is now in “embedded” mode. 22-Nov-2007We now have a mailing list for OCMock. Please send an empty message to ocmock-subscribe@mulle-kybernetik.com to subscribe. The actual mailing list address is ocmock@mulle-kybernetik.com but you must be subscribed to be able to post. (Too much hassle with spam otherwise.) 21-Jun-2007New release (1.17) which combines several contributions. Added nice mocks, which ignore unexpected invocations, and exceptions meant to cause the test to fail fast are now rethrown in verify. 11-Jun-2006New release (1.12) which combines several contributions. Added support for stubbing primitive return types, matching nil and struct arguments, and throwing exceptions. 03-Oct-2005New release (1.10) which has support for mocking protocols. Also added XCode 2.1 compliant project files and moved to built-in OCUnit. 26-Sep-2004Changed a small but important detail: The MockObject and MockRecorder classes now inherit from NSProxy which carries much less baggage in terms of methods that cannot be mocked because they are defined by the base class. 30-Aug-2004First public release. I am using the Subversion revision numbers to version the releases, which is why we start with 1.4 and not 1.0 as one could expect. 23-Jul-2004While working on a new Objective-C project, a Mac OS X monitor for Damage Control, I decide that I've gotten so used to developing test-first and using mock objects that I will bite the bullet and do a simple mock objects implementation in Objective-C. It'll be basic but since Objective-C is so dynamic I probably only need a fraction of the code needed for the Java and .NET versions. Stable ReleasesThe binaries require the lowest version of OS X and the iOS SDK that supports all features. It is usually possible to build a given version of OCMock from source for older versions of OS X at the expense of losing some features.
Source codeThe source code for OCMock is available on GitHub
https://github.com/erikdoe/ocmock
LicenseCopyright (c) 2004 - 2012 by Mulle Kybernetik. All rights reserved. Permission to use, copy, modify and distribute this software and its documentation is hereby granted, provided that both the copyright notice and this permission notice appear in all copies of the software, derivative works or modified versions, and any portions thereof, and that both notices appear in supporting documentation, and that credit is given to Mulle Kybernetik in all documents and publicity pertaining to direct or indirect use of this code or its derivatives. THIS IS EXPERIMENTAL SOFTWARE AND IT IS KNOWN TO HAVE BUGS, SOME OF WHICH MAY HAVE SERIOUS CONSEQUENCES. THE COPYRIGHT HOLDER ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION. THE COPYRIGHT HOLDER DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE OR OF ANY DERIVATIVE WORK.
Highlighted features were added in the latest few releases.
Class mocks
Creates a mock object that can be used as if it were an instance of SomeClass. Expectations and verification
Tells the mock object that someMethod: should be called with an argument that is equal to someArgument. After this setup the functionality under test should be invoked followed by
The verify method will raise an exception if the expected method has not been invoked. Stubs
Tells the mock object that when someMethod: is called with someArgument it should return aValue. If the method returns a primitive type the return value must be wrapped as follows:
Values can also be returned in pass-by-reference arguments:
In this case the mock object will set the reference that is passed to the method to aValue, which currently has to be an object. The mock object can also throw an exception or post a notification when a method is called:
In fact, the notification can be posted in addition to returning a value:
The mock can delegate the handling of an invocation to a completely different method:
In this case the mock object will call aMethod: on anObject when someMethod: is called. The signature of the replacement method must be the same as that of the method that is replaced. Arguments will be passed and the return value of the replacement method is returned from the stubbed method. If Objective-C blocks are available a block can be used to handle the invocation and set up a return value:
If using a partial mock (see below) it is possible to forward the method to the implementation in the real object, which can be useful to simply check that a method was called:
Note that it is possible to use andReturn:, andThrow:, etc with expect, too. This will then return the given return value and, on verify, ensure that the method has been called. Argument constraints
Tells the mock object that someMethod: should be called and it does not matter what the argument is. Pointers require special treatment:
Other constraints available are:
The last constraint will, when the mock object receives someMethod:, send aSelector to anObject and if aSelector takes an argument will pass the argument that was passed to someMethod:. The method should return a boolean indicating whether the argument matched the expectation or not. If Objective-C blocks are available it is possible to check the argument with a block as follows:
Last but not least it is also possible to use Hamcrest matchers like this:
Note that this will only work when the Hamcrest framework is explicitly linked by the unit test bundle. Nice mocks / failing fastWhen a method is called on a mock object that has not been set up with either expect or stub the mock object will raise an exception. This fail-fast mode can be turned off by creating a "nice" mock:
While nice mocks will simply ignore all unexpected methods it is possible to disallow specific methods:
Note that in fail-fast mode, if the exception is ignored, it will be rethrown when verify is called. This makes it possible to ensure that unwanted invocations from notifications etc. can be detected. Protocol mocks
Creates a mock object that can be used as if it were an instance of an object that implements SomeProtocol. Partial mocks
Creates a mock object that can be used in the same way as anObject. When a method that is not stubbed is invoked it will be forwarded to anObject. When a stubbed method is invoked using a reference to anObject, rather than the mock, it will still be handled by the mock. Note that currently partial mocks cannot be created for instances of toll-free bridged classes, e.g. NSString. Observer mocks
Creates a mock object that can be used to observe notifications. The mock must be registered in order to receive notifications:
Expectations can then be set up as follows:
Note that currently there is no "nice" mode for observer mocks, they will always raise an exception when an unexpected notification is received. Instance-based method swizzlingIn a nutshell, Method Swizzling describes the replacement of a method implementation with a different implementation at runtime. Using partial mocks and the andCall: stub OCMock allows such replacements on a per-instance basis.
After these two lines, when someMethod: is sent to anObject the implementation of that method is not invoked. Instead, differentMethod: is called on differentObject. Other instances of the same class are not affected; for these the original implementation of someMethod: is still invoked. The methods can have different names but their signatures should be the same. More detailThe test cases in OCMockObjectTests and OCMockObjectHamcrestTests show all uses of OCMock. Changes.txt contains a chronological list of all changes.
This section was written for Xcode 4.2 and iOS 5. It applies to Xcode 4.3 and iOS 5.1, too.
OCMock static libraryOCMock comes in two flavours, a framework for use in OS X projects and a static library for use in iOS projects. You might find references to so-called "logic tests" for the iPhone that worked with the framework. This is outdated. Today we always use the static library for iOS development. When a test project links against the static library the OCMock classes are included in the binary itself, which means they can be run in the simulator or an iOS device. The library included in the binary releases of OCMock is built "fat", containing binary code for i386, which is required for the simulator, and armv7, which is required for running on the device. Adding the library to a projectTo use the library in an iOS project, you have to make the actual library, ie. libOCMock.a, as well as the header files available to the target containing the tests. In the iOS5 example project we're using this directory structure:
The library must be added to the the test target in the "Link Binaries With Libraries" build phase. Click on the plus (+) icon, choose "Add other..." and find the library in the filesystem.
Adding the library this way automatically adds a library search path to the project so that the linker can find the library. Unfortunately, it does not add a header search path, which is needed for the compiler to resolve includes/imports. This must be done manually in the build settings. Search for "header" which should bring up the "Header Search Paths" setting. Following the directory layout above the best way to point to the header directory is by adding a reference relative to the project's source directory. Don't be confused, the table view will show what that resolves to, but the setting saved does retain the variable.
One further step is required to work around an issue in the linker. For static libraries the linker tries to be clever and only includes those symbols that it thinks are used. It gets this wrong with Objective-C categories, and we need to tell it that (a) we're dealing with Objective-C and (b) that it should load all symbols for the OCMock library.
If you forget this step or you get the force load path wrong you will get an appropriate warning (in the form of an exception) the first time you try to use a mock object in your tests. Different linker flags have been suggested but I do recommend -force_load; it works, as long as the path following it is correct. More details in Apple's Technical Q&A QA1490. If you have copied the library or headers to a different place all of these settings must be adjusted accordingly. The iPhoneExample projectOCMock now contains an iOS5 example project that shows how to use OCMock in iOS application tests. The project is set up following Apple's guidelines and the linker flags are set as described above. Note that in the Xcode table view the path variables are expanded and the full paths are shown. This can be confusing. The project contains one unit test that shows how to use mocks to replace a UITableView when testing a controller. This project should build and the test should pass. If it doesn't something is very wrong. If you modify the test to fail, for example by changing the call to the method under test, the most useful output is available from the log section with the output expanded:
More supportIf you have any questions or issues, please use the OCMock forum or Stackoverflow. I'm trying to monitor both but things can get busy for me... Poking Objective-C with a Testing StickGeneral tutorial that explains in detail how to set up a project for testing with OCUnit and OCMock. Also shows how partial mocks can be used to substitute individual methods in a test. Testing Cocoa ControllersA tutorial that shows how to test Cocoa controller objects without having to load the user interface from the NIB file. The objects defined in InterfaceBuilder are replaced with mocks. Mocking Singletons with OCMockThe title says it all. Uses categories to allow substitution of system-provided singletons. Writing custom constraintsOCMock integrates with Hamcrest but sometimes it can be easier to write a constraint using the API used by OCMock's built-in constraints. This tutorial shows how it's done. Applies to iPhone and Cocoa development. OCMock and the iPhoneThis tutorial shows how to use OCMock as a framework for an iPhone project. There are some limitations to this approach. Please visit the "iPhone/iOS" tab for more information. Mocking a database connectionThis tutorial describes a classic use case for mock objects, it shows how to replace a dependency on an external call with a mock object that returns canned values. This tutorial is in German. For feedback and questions please use the OCMock forum on this site. For contributions and discussions of future development of OCMock please join the mailing list by sending an empty message to ocmock-subscribe@mulle-kybernetik.com. You can also contact me directly by email. |