I have a method that I need to stub. The method is of the form below:
BOOL myMethodWithError:(*__autoreleasing *NSError)error;
So I mocked the object and attempted to return a nil back through 'error'. I coded it as follows.
id mockMyObject = [OCMockObject mockForClass:[MyObject class]];
BOOL retVal = YES;
NSError *error = nil;
[[[mockMyObject stub] andReturn:OCMOCK_VALUE(retVal)] myMethodWithError:&error];
When the test is run and the mock object is operated, the error reference id appears to change. So the mock throws an exception:
OCMockObject[MyObject]: expected method invoked:
myMethodWithError:0xbfffca78
I have tried a number of different ways but each time the pointer value appears to change once the error object is passed to the method which causes the mock object to throw an error.
I simply need to test my business rules against the pass-by-reference value of the argument, but I can't seem to get the mock or test object to cooperate.
Thanks in advance, any help will be greatly appreciated.
Apart from the solutions Ben mentions in his answer(I only tested the one based on ignoringNonObjectArgs, and it works fine), I prefer to use [OCMArg anyPointer] with the appropriate cast:
[[[myMock stub] andReturn:something] someMethod:(NSError * __autoreleasing *)[OCMArg anyPointer]];
Use ignoringNonObjectArgs
[[[[mock stub] andReturnValue:OCMOCK_VALUE((BOOL){YES})] ignoringNonObjectArgs] myMethodWithError:NULL];
From http://ocmock.org/features/
Arguments that are neither objects nor pointers or selectors cannot be
ignored using an any placeholder. It is possible, though, to tell the
mock to ignore all non-object arguments in an invocation:
[[[mock expect] ignoringNonObjectArgs] someMethodWithIntArgument:0]
In this
case the mock will accept any invocation of someMethodWithIntArgument:
no matter what argument is actually passed. If the method has object
arguments as well as non-object arguments, the object arguments can
still be constrained as usual using the methods on OCMArg.
Bonus Answer
This would also solve your issue:
[[[mock stub] andReturnValue:OCMOCK_VALUE((BOOL){YES})] myMethodWithError:[OCMArg setTo:nil]];
Related
I have a class and this class has a method ("original_method") that uses two objects of different types. In this method there are two calls: one call to a method of the first object that returns a value which is then used for calling a method of the second object. I was wondering what is the correct way of unit testing such behavior (using google-test). Specifically, I want to test that the argument provided to the second object is indeed the value returned from the first.
Currently I achieve this using parametrized tests - the code below shows what I do:
TEST_P(SomeTestSuite, checkingIfCalledWithTheRightArgument)
{
EXPECT_CALL(*obj1, get_some_value()).WillOnce(Return(name_of_value));
EXPECT_CALL(*obj2, do_a_calculation(name_of_value));
obj0->call_original_method();
}
I have a fixture for my original class under testing, i have mocks for obj1 and obj2, I provide a value for "name_of_value" in the parameters and the test works.
My problem is that this doesn't seem to be the correct way, I believe I shouldn't have to pass a parameter to check such a functionality. I would appreciate if somebody could explain to me how i should have approached the problem.
Thank you.
EDIT:
I think I could do:
TEST_F(SomeTestSuite, checkingIfCalledWithTheRightArgument)
{
EXPECT_CALL(*obj1, get_some_value());
auto name_of_value = obj1->get_some_value();
EXPECT_CALL(*obj2, do_a_calculation(name_of_value));
obj0->call_original_method();
}
but I'm not sure if this captures (or actually tests) the original behaviour...
I am a newbie to Jasmine and a bit confused between above two functions. My sole purpose is to give a fake implementation to a spy function. But, If I put debugger in callFake the it is getting called but and.stub's function is not getting called. Could anyone please explain what is the difference between these two functions.
spyOn(manager, 'getUsers').and.stub(function () {
//to do
});
vs
spyOn(manager, 'getUsers').and.callFake(function () {
//to do
});
Looking at the documentation located at https://jasmine.github.io/2.0/introduction.html#section-Spies, when you spyOn something it logs of all the calls being made on the spied on object method. This means that it is calling the actual method of the object, but keeping track of what calls were made.
If you want to allow using the original object, but don't want specific methods to be called, you have the options of using and.callFake and and.stub. The differences are in the method signatures.
callFake takes a function as a parameter. This allows you to fake the method call and return a value of your desire.
original method signature is myMethod(param1: string): string
spyOn(service, 'myMethod').and.callFake((param1) => {
expect(param1).toBe('value');
return 'returnValue';
});
stub has no parameters and merely intercepts the call to the method
spyOn(service, 'myMethod').and.stub();
myMethod can have parameters and can have a return type, but it doesn't matter since stub just intercepts the call and will return null if there is a return type.
In both instances, the method calls are logged and you can then do something like expect(service.myMethod).toHaveBeenCalled() or expect(service.myMethod).toHaveBeenCalledWith('value')
this is a sample code
when(someObject.someMethod(any(AbstractClass.class)).thenReturn(mockvalue);
in the above code, it isn't recognising the argument any(AbstractClass.class) and it calls the real method instead of returning the mock value.
I am sorry, but you are on the wrong track there.
any( SomeClass.class ) does NOT do what you believe it does. It especially does NOT check if the argument is a SomeClass, see the Javadoc:
Any kind object, not necessary of the given class.
The class argument is provided only to avoid casting.
If you have a look at the Any class, you will see why:
public boolean matches(Object actual) {
return true;
}
So, ANY argument (as the name implies) will be accepted there. In your case, this means that IF the method someMethod on that specific someObject is called, it WILL return the mockvalue, no matter what the actual argument is.
This implies that your problem is somewhere else entirely, for example ...
Your mock is not correctly injected into the class you are testing (so that the class is using another object and not the mock)
The method in question isn't actually called (for example there could be another one with a similar signature, etc.).
Hard to say without code. I would ask a new question but provide more code this time.
Does anyone know what the numbers 5b40c281 and 78a1d1f4 mean in the EasyMock test case fail shown below?
Are they essentially address pointers to two different instances of PdlPrintJob?
Does anyone know why this fail is occurring?
In the main code, PdlPrintJob is constructed (using new PdlPrintJob()) and passed as a parameter to method printer.executePrintJob().
In the test case, PdlPrintJob is constructed (using new PdlPrintJob()) and passed as a parameter to mockPrinter.executePrintJob().
Thanks for any advice,
Best regards
James
junit.framework.AssertionFailedError:
Unexpected method call executePrintJob(com.canon.cel.meap.jobs.PdlPrintJob#5b40c281, EasyMock for interface com.canon.meap.security.AccessControlToken):
executePrintJob(com.canon.cel.meap.jobs.PdlPrintJob#5b40c281, EasyMock for interface com.canon.meap.security.AccessControlToken): expected: 0, actual: 1
executePrintJob(com.canon.cel.meap.jobs.PdlPrintJob#78a1d1f4, EasyMock for interface com.canon.meap.security.AccessControlToken): expected: 1, actual: 0
Its because you have done something like this in your test class.
EasyMock.expect(executePrintJob(new PdlPrintJob(),....))'
but actually it should have been a mockObject that you should have passed as parameter.
you need to do something like this
PdlPrintJob pdlPrintJob=Easymock.createNiceMock(PdlPrintJob.class);
Powermock.expectNew(PdlPrintJob).andReturn(pdlPrintJob).anyTimes(); //this will return the mocked instance of PDlPrintJob class wherever 'new' operator is used for this class
EasyMock.expect(executePrintJob(pdlPrintJob,.....)).andReturn(anythingYouWantToReturn).anyTimes(); // have added '.....' in case there are other parameters to this method
EasyMock.replay(pdlPrintJob);
Powermock.replayAll();
You were facing the issue because Easymock is a strict mocking framework, you had asked it to expect a particular method with particular object type only (its like tightly binding method expectation to a single object), and during execution as new operator was used the method expectation failed as object parameters didnt match the expectation of Easymock, resulting in this exception.
I always prefer doing something like this for method expectation
if my method to be tested is
public String compress(String str, Integer intr, double ch){}
I expect this method in easymock as follows:
EasyMock.expect(compress(EasyMock.anyObject(String.class),EasyMock.anyObject(Integer.class),EasyMock.anyDouble())).andReturn("Done compressing").anyTimes();
so by this approach my method expectation works for any valid parameters passed to my compress() method during test case execution.
Hope that helps!
Good luck!
Not sure how I should be asking the question, but when I define my mock objects, and somewhere in the code it attempts to cast it to a different type the test throws me
$Proxy6 cannot be cast to ...
How does one solve this problem?
Does this class really need to be mocked? I usually mock services and use concrete classes for value types passed in.
One thing you can do is outlined here: define an interface in your test.
If it really needs to be mocked and you can't do the above you could provide your own implementation which does what you want the mock to do e.g. records values passed in, methods called, returns the values you want etc. and assert what you need at the end - that might be a lot of work though.
Lastly, is this pointing you towards some unidentified interfaces in your design or that the code under test needs some refactoring?
As always, the test is telling you something about your design. Why is the code trying to cast the object? Could you give us more detail?