Although I have seen similar questions in SO, none of the answers seems to solve my problem.
I have a NSManagedObject class generated by mogenerator with custom functions (not in the model):
#interface MyManagedClass : _MyManagedClass {
-(NSNumber*)getRandomNumber2;
-(void)function_I_want_to_test;
}
My function_I_want_to_test() depend on the result of random(), and that is something I must control during testing. Since I cannot mock random() I built a function wrapper, which by the way, is not static because I had many problems with OCMock and static class functions.
The setup of my unit test looks like:
[MagicalRecord setDefaultModelFromClass:[self class]];
[MagicalRecord setupCoreDataStackWithInMemoryStore];
Using the debugger I could verify that the model is properly loaded. Also if I do it the non magical way:
NSBundle *b = [NSBundle bundleForClass:[self class]];
model = [NSManagedObjectModel mergedModelFromBundles:#[b]];
After this point I cannot create any mock to stub my random() wrapper function
I have tried a class mock
id mock = [OCMockObject mockForClass:[MyManagedClass class]];
[[[mock stub] andReturn:#50] getRandomNumber2];
MyManagedClass *my_object = [mock MR_createEntity];
I have tried using a partial mock
MyManagedClass *my_object = [MyManagedClass MR_createEntity];
id mock2 = [OCMockObject partialMockForObject:my_object];
After last point, just creating an instance of mock2 destroys the dynamic properties of my_object, and it become useless.
I have also tried using a mock protocol with the function I want to stub, still to no avail.
The runtime exception is the normal one other people get when using with tests with Core Data objects: the properties are not recognized selectors.
However the strange thing to me is that I am not trying to stub any dynamic property, but a normal, compile time known function. Hence it seems strange to me that using OCMock renders my instances useless.
Ideally I would like something that uses OCMock/Mogenerator/Magicalrecord.
what am I doing wrong?
I recommend against trying to mock out managed objects. There's a lot of runtime craziness going on to make managed objects work. That's why I suggest going with an in memory database approach for testing. This will let you create empty instances of your entities while letting the core data stuff happen.
Since you're probably using unit tests, I suggest that in each test case where you think you need to mock out some data to instead recreate your whole stack, and set a new entity up with the state it needs to run your test. You can also make a test in memory persistent store separate from the one the default stack create method gives you, and attach this second one to your default stack. That is, make a new in memory store, initialize it with your fake/mock data entities, and attach that to your test data stack.
I hope this rambling helps a bit, but the bottom line is dont mock managed objects...really.
You could do it by moving your random number generation out of the Core Data object and into a helper class:
#implementation RandomNumberGenerator
static RandomNumberGenerator *_sharedInstance = nil;
+(RandomNumberGenerator *)sharedInstance {
if (_sharedInstance == nil) {
_sharedInstance = [[RandomNumberGenerator alloc] init];
}
return _sharedInstance;
}
+(void)setSharedInstance:(RandomNumberGenerator *)instance {
[instance retain];
[_sharedInstance release];
_sharedInstance = instance;
}
-(NSNumber *)generateRandomNumber {
return ...
}
#end
Then in your test:
-(void)testSomething {
id mockGenerator = [OCMockObject mockForClass:[RandomNumberGenerator class]];
[RandomNumberGenerator setSharedInstance:mockGenerator];
[[[mockGenerator stub] andReturn:#(4)] generateRandomNumber];
MyManagedClass *my_object = [MyManagedClass MR_createEntity];
expect(my_object.someField).to.equal(someValueBasedOnGeneratedRandomNumber);
}
Related
I just upgraded to XCode 7 recently, forcing me to upgrade OCMock so that I have support for x64 architectures. Apparently a change was made in OCMock which does not allow for a previously mocked object to be remocked-- in other words, I had a helper method that did something like this:
-(MyObject *)getObject {
Factory *factory = [self.dependencyInjector getInstance:factory];
id mockFactory = [OCMockObject partialMockForObject:factory];
[[[mockFactory stub] andReturn:#"important-value"] thing];
return [[MYObject alloc] initWithFactory:mockFactory];
}
This worked fine previously, but apparently there was a change to OCMockObject to not allow a re-mocking of an already mocked object. Since the factory object returned by the injector is effectively a singleton, subsequent calls to the getObject method is calling partialMockForObject: on it multiple times and this now throws an exception "Another mock is already associated with object".
Is there any way to make OCMock not throw an error? I tried calling stopMocking on the object prior to mocking it, but that does not fix this issue. The only way around it was to do something like:
-(MyObject *)getObject {
if (!self.mockFactory) {
Factory *factory = [self.dependencyInjector getInstance:factory];
id mockFactory = [OCMockObject partialMockForObject:factory];
[[[mockFactory stub] andReturn:#"important-value"] thing];
self.mockFactory = mockFactory;
}
return [[MYObject alloc] initWithFactory:self.mockFactory];
}
which is really annoying to have to do...
Looking at the code as it is today, the implementation of stopMocking explicitly resets the associated object (https://github.com/erikdoe/ocmock/blob/dd5599695dcc50afe4d6bdff509ed3cbe389c667/Source/OCMock/OCPartialMockObject.m#L80). I'm at a loss how calling stopMocking doesn't solve the problem. Can you build a debug version of OCMock and set a breakpoint at the line highlighted above and see whether it's called?
I would like to accomplish what also is described here, i.e create mocks inside legacy code. However I require partial instead of nice or strict mocks.
For example, consider leaderboards that behave exactly like GKLeaderbaord except for implementing a stubbed version of loadScoresWithCompletionHandler:.
I've tried this code inside an XCTestCase but it currently fails at runtime in the indicated line: OCMInvocationMatcher raises an EXC_BAD_ACCESS error. Perhaps there is some infinite recursion going on.
id leaderboardMock = OCMClassMock(GKLeaderboard.class);
OCMStub([leaderboardMock alloc])
.andReturn(OCMPartialMock([GKLeaderboard alloc]));
OCMStub([leaderboardMock loadScoresWithCompletionHandler: [OCMArg any]])
.andDo(^(NSInvocation *invocation) { /* ... */ });
// these parts normally nested inside legacy code
GKLeaderboard *leaderboard = /* raises EXC_BAD_ACCESS */
[[GKLeaderboard alloc] initWithPlayers: #[ GKLocalPlayer.localPlayer ]];
leaderboard.identifier = #"Test";
[leaderboard loadScoresWithCompletionHandler: nil /* ... */ ];
What am I doing wrong and is this even possible for partial mockups?
UPDATE I can by now see how the indicated line might (quite obviously) cause an infinite recursion, but don't yet know how to avoid (or break) it.
UPDATE I've also had no success with an attempt of bringing in an dedicated class with OCMStub([leaderboardMock alloc]).andReturn([LeaderboardMock alloc]) (nor with OCMStub([leaderboardMock initWithPlayers: [OCMArg any]]).andReturn([[LeaderboardMock alloc] initWithPlayers:nil])). Perhaps OCMock does its magic at the level of init (the documentation says: "it is not possible to stub the init method, because that is implemented by the mock itself") hence such an attempt the level of alloc (or initWithPlayers:) cannot have its desired effect.
Not sure I follow what you are trying to do. It seems like a misunderstanding. Does the following not work for you?
GKLeaderboard *leaderboard = [[GKLeaderboard alloc] initWithPlayers: ... ];
id leaderboardMock = OCMPartialMock(leaderboard);
OCMStub([leaderboarMock loadScoresWithCompletionHandler: ...]);
You can use the normal object without restrictions. You can use the partial mock created for the object to manipulate the actual instance in leaderboard. That's the beauty of partial mocks.
UPDATE: If the object creation is not under your control, you can try the following:
GKLeaderboard *leaderboard = [[GKLeaderboard alloc] initWithPlayers: ... ];
id leaderboardMock = OCMPartialMock(leaderboard);
OCMStub([leaderboardMock alloc]).andReturn(leaderboardMock);
OCMStub([leaderboardMock initWithPlayers:[OCMArg any]).andReturn(leaderboard);
OCMStub([leaderboarMock loadScoresWithCompletionHandler: ...]);
I have by now concluded that method swizzling would be a possible choice.
A replacement method could e.g. generate a partial mockup from within the context of legacy code and hence introduce a partial mock in that context without requiring changes to legacy APIs.
you should not use following line, it will mock your entire class and none of real object will get called.
OCMClassMock(GKLeaderboard.class)
As per my previous question, here, I've adapted my Data Controller class over to use a singleton design pattern so that I can use it only once across multiple views. However I do have a couple question I can't seem to find the solution too.
Firstly I'm not exactly sure how to call the class/object in the two views to make it work, and secondly I've made the initialisation method global with + but do I need to do this with each of the methods?
The initialisation of of the class that I want to be able to share across the views, in order to share the data, is
static SpeecherDataController *_instance = nil; // <-- important
+(SpeecherDataController *)instance
{
// skip everything
if(_instance) return _instance;
// Singleton
#synchronized([SpeecherDataController class])
{
if(!_instance)
{
_instance = [[self alloc] init];
// NSLog(#"Creating global instance!"); <-- You should see this once only in your program
}
return _instance;
}
return nil;
}
The class uses three Mutable Arrays as the main content which need to be both set and read in the two views.
If I understand your questions correctly, I think the answers are:
You can use something like:
SpeecherDataController * localReference = [SpeecherDataController instance];
and then later:
[localReference someMessage:param]; // or ...
localReference.property = whatever;
No, the methods on your SpeecherDataController class do not also need to be made class methods (i.e., they do not need to have the + prefix, they can use - if you want to access ivars within them).
Note: I think you want to replace [[self alloc] init]; with [[SpeecherDataController alloc] init]; in your implementation of instance.
(Also, note: I was unable to follow your link to "here" above to see your previous question. So my apologies if I misunderstood something.)
I have a number of Objective-C classes organized in an inheritance hierarchy. They all share a common parent which implements all the behaviors shared among the children. Each child class defines a few methods that make it work, and the parent class raises an exception for the methods designed to be implemented/overridden by its children. This effectively makes the parent a pseudo-abstract class (since it's useless on its own) even though Objective-C doesn't explicitly support abstract classes.
The crux of this problem is that I'm unit testing this class hierarchy using OCUnit, and the tests are structured similarly: one test class that exercises the common behavior, with a subclass corresponding to each of the child classes under test. However, running the test cases on the (effectively abstract) parent class is problematic, since the unit tests will fail in spectacular fashion without the key methods. (The alternative of repeating the common tests across 5 test classes is not really an acceptable option.)
The non-ideal solution I've been using is to check (in each test method) whether the instance is the parent test class, and bail out if it is. This leads to repeated code in every test method, a problem that becomes increasingly annoying if one's unit tests are highly granular. In addition, all such tests are still executed and reported as successes, skewing the number of meaningful tests that were actually run.
What I'd prefer is a way to signal to OCUnit "Don't run any tests in this class, only run them in its child classes." To my knowledge, there isn't (yet) a way to do that, something similar to a +(BOOL)isAbstractTest method I can implement/override. Any ideas on a better way to solve this problem with minimal repetition? Does OCUnit have any ability to flag a test class in this way, or is it time to file a Radar?
Edit: Here's a link to the test code in question. Notice the frequent repetition of if (...) return; to start a method, including use of the NonConcreteClass() macro for brevity.
Here's a simple strategy that worked for me. Just override invokeTest in your AbstractTestCase as follows:
- (void) invokeTest {
BOOL abstractTest = [self isMemberOfClass:[AbstractTestCase class]];
if(!abstractTest) [super invokeTest];
}
You could also override + (id)defaultTestSuite method in your abstract TestCase class.
+ (id)defaultTestSuite {
if ([self isEqual:[AbstractTestCase class]]) {
return nil;
}
return [super defaultTestSuite];
}
It sounds like you want a parameterized test.
Parameterized tests are great whenever you want to have a large number of tests with the same logic but different variables. In this case, the parameter to your test would be the concrete tested class, or possibly a block that will create a new instance of it.
There's an article about implementing parameterized testing in OCUnit here. Here's an example of applying it to testing a class hierarchy:
#implementation MyTestCase {
RPValue*(^_createInstance)(void);
MyClass *_instance;
}
+ (id)defaultTestSuite
{
SenTestSuite *testSuite = [[SenTestSuite alloc] initWithName:NSStringFromClass(self)];
[self suite:testSuite addTestWithBlock:^id{
return [[MyClass1 alloc] initWithAnArgument:someArgument];
}];
[self suite:testSuite addTestWithBlock:^id{
return [[MyClass2 alloc] initWithAnotherArgument:someOtherArgument];
}];
return testSuite;
}
+ (void)suite:(SenTestSuite *)testSuite addTestWithBlock:(id(^)(void))block
{
for (NSInvocation *testInvocation in [self testInvocations]) {
[testSuite addTest:[[self alloc] initWithInvocation:testInvocation block:block]];
}
}
- (id)initWithInvocation:(NSInvocation *)anInvocation block:(id(^)(void))block
{
self = [super initWithInvocation:anInvocation];
if (!self)
return nil;
_createInstance = block;
return self;
}
- (void)setUp
{
_value = _createInstance();
}
- (void)tearDown
{
_value = nil;
}
The simplest way:
- (void)invokeTest {
[self isMemberOfClass:[AbstractClass class]] ?: [super invokeTest];
}
Copy, paste and replace AbstractClass.
I don't see a way to improve on the way you're currently doing things without digging into OCUnit itself, specifically the SenTestCase implementation of -performTest:. You'd be set if it called invoked a method to determine "Should I run this test?" The default implementation would return YES, while your version would be like your if-statement.
I'd file a Radar. The worst that could happen is your code stays the way it is now.
Before working with Objective-C and Core Data, I had occasions to create classes that needed to be initialized with certain parameters that, after initialization, could not be modified (though they could be read).
With Core Data, I believe I can create a customized init on my NSManagedObject derived class as long as it includes a way to insert the object into a context like such:
-(Cell*) initWithOwner:(CellList*)ownerCellList andLocation:(int)initLocation
{
if (self = (Cell*) [NSEntityDescription insertNewObjectForEntityForName:#"Cell"
inManagedObjectContext:[ownerCellList managedObjectContext]])
{
self.location = [NSNumber numberWithInt:initLocation];
self.owner = ownerCellList;
[ownerCellList addCellListObject:self];
}
return self;
}
Normally, I'd have a location variable and the location property would be read-only (so once set at initialization, it could not be changed). Is there a way to get this sort of pattern with Core Data? Is there a better way I'm not thinking of?
Thanks!
You are correct. As long as your initializer calls the NSManagedObject's designated initializer, your approach is fine. You can also override the -[NSManagedObject awakeFromInsert] to perform some action after insertion (creation) or -[NSManagedObject awakeFromFetch] to perform an action (e.g. populating a cache) each time the object is faulted back into a managed object context.
Like the rest of Objective-C, there is no way to make a property truly readonly. Malicious code will likely be able to modify your property. However, in your custom class, you can declare a #property(readonly) for e.g. location. This will at least cause a warning if you try to modify the property and will signal your intent to client code.
For anyone who stumbles here, reads the comments, and wonders at the final answer, it should be something like this. Continuing with the example above, it would be:
-(Cell*) initWithOwner:(CellList*)ownerCellList andLocation:(int)initLocation
{
NSManagedObjectContext *context = [ownerCellList managedObjectContext];
NSManagedObjectModel *managedObjectModel =
[[context persistentStoreCoordinator] managedObjectModel];
NSEntityDescription *entity =
[[managedObjectModel entitiesByName] objectForKey:#"Cell"];
self = [self initWithEntity:entity inManagedObjectContext:context];
if (self)
{
self.location = [NSNumber numberWithInt:initLocation];
self.owner = ownerCellList;
[ownerCellList addCellListObject:self];
}
return self;
}
NSEntityDescription's insertNewObjectForEntityForName:inManagedObjectContext: documentation says this is roughly how it converts from a given entityName (#"Cell") and a context (from ownerCellList) to an NSManagedObject instance.