I still have some difficulties with Mockito. I want to have two test cases for two different object examples. So I want to simulate different method behaviour depending on argument value.
The problem is that when I run test() method, the returned value of help valiable is "b" and the assertion doesn't return true. If I comment the line marked as (***), everything works fine.
As you can see I tried to use thenAnswer instead of thenReturn, but the result was the same.
public class TestItAll {
TestClass test;
HelpClass a ;
HelpClass b;
#Before
public void init(){
a = new HelpClass("a");
b = new HelpClass("b");
Mockito.when(test.getHelp(a)).thenReturn("a");
/*Mockito.when(test.getHelp(a)).thenAnswer(
new Answer< String>() {
public String answer(InvocationOnMock invocation) {
return "a";
}
}); */
Mockito.when(test.getHelp(b)).thenReturn("b");//(***)
/*Mockito.when(test.getHelp(b)).thenAnswer(
new Answer< String>() {
public String answer(InvocationOnMock invocation) {
return "b";
}
}); */
}
#Test
public void testA(){
String help= test.getHelp(a);
Assert.assertEquals(help, "a");
}
/*#Test
public void testB(){
String help= test.getHelp(b);
Assert.assertEquals(help, "b");
}*/
}
Please, don't ask me why I'm mocking a test object. It's just a model example of a more complicated situation.
Firstly, I assume that your declaration TestClass test; is in fact TestClass test = mock(TestClass.class);, otherwise the #Before method throws NullPointerException.
When using when(test.getHelp(a)) mockito will use a's equals method to check whether the parameter matched. If e.g. equals method always returns true, it won't be able to differ a from b. I have run your code with overriding equals method (i.e. HelpClass objects are equal only if they are the same instance) and both tests have passed.
You may want to use argument matcher - when(test.getHelp(argThat(sameInstance(a)))) to not rely on your equals method. If you need something more complex than sameInstance, I would recommend sameBeanAs matcher from shazamcrest.
Related
I am trying to test following method:
public void execute(Publisher<T> publisher) throws Exception {
PublishStrategy<T> publishStrategy = publisher.getPublishStrategy();
publishStrategy.execute(publisher::executionHandler);
}
Following is the Junit code:
#Test
public void testExecute() throws Exception {
PublishStrategy<Event> publishStrategy = Mockito.mock(PublishStrategy.class);
Publisher<Event> publisher = Mockito.mock(Publisher.class);
Mockito.when(publisher.getPublishStrategy()).thenReturn(publishStrategy);
Mockito.doNothing().when(publishStrategy).execute(publisher::executionHandler);
PublishJob job = new PublishJob(publisher);
job.execute(publisher);
Mockito.verify(publishStrategy, Mockito.times(1)).execute(publisher::executionHandler);
}
On the verify method call, I am getting following mockito exception:
Argument(s) are different! Wanted:
publishStrategy.execute(
com.test.producer.jobs.PublishJobTest$$Lambda$3/1146825051#6f45df59
);
-> at com.test.producer.jobs.PublishJobTest.testExecute(PublishJobTest.java:23)
Actual invocation has different arguments:
publishStrategy.execute(
com.producer.jobs.PublishJob$$Lambda$2/1525409936#38e79ae3
);
-> at com.producer.jobs.PublishJob.execute(PublishJob.java:30)
I don't understand why mockito considers both the lambda's are different?
Update
I solved it without using Mockito.
Here's the other approach. Omitted empty overridden methods:
#Test
public void testExecute() throws Exception {
PublishStrategy<Event> publishStrategy = new PublishStrategy<Event>() {
#Override
public void execute(Consumer<List<Event>> handler) {
Assert.assertNotNull(handler);
}
};
Publisher<Event> publisher = new AbstractPublisher<Event>() {
#Override
public void init(PublishStrategy<Event> publishStrategy) {
this.publishStrategy = publishStrategy;
}
#Override
public void executionHandler(List<IngestEvent> items) {
}
#Override
public PublishStrategy<IngestEvent> getPublishStrategy() {
return this.publishStrategy;
}
};
publisher.init(publishStrategy);
PublishJob job = new PublishJob();
job.execute(publisher);
}
In Java, two objects aren't equal because they are instances of the same class! Objects are equal because calling a.equals(b) returns true!
In your case, that class PublishJob probably doesn't override the equals method. So, comparing two lambda instances results in false. And note: I really don't see how you could fix this by adding an equals method.
In other words: when you do publisher::executionHandler you create two different lambda instances. It doesn't matter that they will both make a call on the same object. You have two lambda instances. And they are simply not equal. And that Mockito method checks for equality.
I think one way how to test this: see if you can get that lambda to be executed. Then verify that the expected call takes place on that object.
I have started learning JUNIT.
Here is what i am trying to achieve.
I have a class which checks if the inputString is part of secretKey;
public class StringChecker {
public boolean isEqual(String name)
{
boolean isEqual = false;
if(getSecretKey().contains(name))
{
isEqual = true;
}
return isEqual;
}
public String getSecretKey()
{
return "OSKAR";
}
}
My test class is this
public class RandomCheck {
#Test
public void isEqualTest()
{
StringChecker stringChecker = mock(StringChecker.class);
when(stringChecker.getSecretKey()).thenReturn("james");
//assertEquals(true, new StringChecker().isEqual("OSKAR")); <----this test case passes
assertEquals(true, stringChecker.isEqual("james"));
}
}
When i use Mocked object it does not give me the expected result, hence failing the test. But when i use a real object it gives me expected result and passes the test.
Am i missing anything? Like any annotation
A mockito mock is an object having the interface of the mocked class, but not its implementation. Your StringChecker is mocked, meaning there is no implementation code making calls from isEqual to getSecretKey as you assume.
You could use mockito spy, See this SO question:
Mockito.spy() is a recommended way of creating partial mocks. The reason is it guarantees real methods are called against correctly constructed object because you're responsible for constructing the object passed to spy() method.
ROOKIE MISTAKE
Here's the rookie mistake i did (mentioned by Arnold).
I mocked the StringChecker class but i did not provide any implementation for isEqual(String) method.
And because there was no implementation, i was getting the default value. In this case false (return type of method is boolean).
Solution
Using static method spy(). (Again mentioned by #Arnold).
So here is what my working code looks like.
#Test
public void isEqualTest()
{
StringChecker stringChecker = new StringChecker();
StringChecker spy = spy(stringChecker);
when(spy.getSecretKey()).thenReturn("james"); // providing implementation for the method
assertEquals(true, spy.isEqual("james"));
}
What i learnt from it.
Just by mocking an object does not get your things done if you intend to use methods of mocked object (In simple terms PROVIDE IMPLEMENTATION for methods of mocked objects).
TIP
If you want to see the default value returned by mocked object, just call the method of mocked object in sysout(without giving implementation).
Hope it will help someone like me.Peace
An alternative way without mocking and with additional test cases:
#Test
public void isEqualTest() {
StringChecker stringChecker = new StringChecker() {
#Override
public String getSecretKey() {
return "james";
}
};
assertTrue(stringChacker.isEqual("james"));
assertTrue(stringChacker.isEqual("jam"));
assertTrue(stringChacker.isEqual("mes"));
assertFalse(stringChacker.isEqual("oops"));
}
BTW, the isEqual() can be simplified to one line:
public boolean isEqual(String name) {
return getSecretKey().contains(name);
}
I am new to Mockito, please help in understanding the basic.
According to me above code is supposed to print 5 when mocked.add(6,7) gets called , but add() method is not getting called and the code prints 0.. why ? any solution for this code ?
import org.mockito.Mockito;
import static org.mockito.Mockito.*;
class Calc{
int add(int a,int b){
System.out.println("add method called");
return a+b;
}
}
class MockTest{
public static void main(String[] args) {
Calc mocked=mock(Calc.class);
when(mocked.add(2,3)).thenReturn(5);
System.out.println(mocked.add(6,7));
}
}
In order to get result of 5, you have to pass the exact params as when you set up the when..then. Otherwise mockito will return a 'default' value (which is 0 for integer:
What values do mocks return by default?
In order to be transparent and unobtrusive all Mockito mocks by
default return 'nice' values. For example: zeros, falseys, empty
collections or nulls. Refer to javadocs about stubbing to see exactly
what values are returned by default.
If you want to return 5 for any integer then use:
when(mocked.add(Mockito.any(Integer.class),Mockito.any(Integer.class))).thenReturn(5);
A "mock" is just an empty dummy object that simulates behaviour of a "real" object. If you define a behaviour such like when(mocked.add(2,3)).thenReturn(5); you specifically tell this mock what to do, when it receives those exact values.
mocked.add(6,7) will return 0 at that point, since you haven't defined its behaviour for those values and therefore uses a default value. So if you want to cover all possible inputs, you can go with the solution #MaciejKowalski posted and use the generic matchers like Mockito.any(Integer.class).
Still I believe it is not clear how to correctly handle mocks. Mocks are a way of providing external dependencies to a system-under-test without the need to set up a whole dependency tree. The real methods inside that class are usually not called. That's what something like when(mocked.add(2,3)).thenReturn(5); means. It tells the mock to behave like the real dependency without actually having it at hand.
An example could look like this:
public class TestClass {
private ExternalDependency dep;
public void setDep(ExternalDependency dep) {
this.dep = dep;
}
public int calculate() {
return 5 + dep.doStuff();
}
}
public class ExternalDependency {
public int doStuff() {
return 3;
}
}
Now in your test code you can use mocks like this:
#Test
public void should_use_external_dependency() {
// Aquire a mocked object of the class
ExternalDependency mockedDep = Mockito.mock(ExternalDependency.class);
// Define its behaviour
Mockito.when(mockedDep.doStuff()).thenReturn(20);
TestClass sut = new TestClass();
sut.setDep(mockedDep);
// should return 25, since we've defined the mocks behaviour to return 20
Assert.assertEquals(25, sut.calculate());
}
If sut.calculate() is invoked, the method in ExternalDependency should not be really called, it delegates to the mocked stub object instead. But if you want to call the real method of the real class, you could use a Spy instead with Mockito.spy(ExternalDependency.class) or you could do that with when(mockedDep.doStuff()).thenCallRealMethod();
I have encountered what I assume might be a bug with Mockito, but was wondering if anyone else can shed light as to why this test doesn't work.
Basically, I have two objects, like this:
public class FirstObject {
private SecondObject secondObject;
public SecondObject getSecondObject() { return secondObject; }
}
public class SecondObject {
private String name;
public String getName() { return name; }
}
The first object is mocked via annotation and the before method:
#Mock
FirstObject mockedFirstObject;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
The second object is mocked in a method:
public SecondObject setupMockedSecondObject() {
SecondObject secondObject = Mockito.mock(SecondObject.class);
Mockito.when(secondObject.getName()).thenReturn("MockObject");
return secondObject;
}
When thenReturn contains a direct call to this method to setup and obtain a mock of the second object, it fails:
#Test
public void notWorkingTest() {
Mockito.when(mockedFirstObject.getSecondObject()).thenReturn(setupMockedSecondObject());
Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}
But, when the mock returned by the same method is assigned to a local variable, which is used in thenReturn, it works:
#Test
public void workingTest() {
SecondObject mockedSecondObject = setupMockedSecondObject();
Mockito.when(mockedFirstObject.getSecondObject()).thenReturn(mockedSecondObject);
Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}
Are we doing something wrong or is this indeed a bug/limitation in Mockito? Is there a deliberate reason for this not working?
This is indeed a limitation of Mockito, and it is referenced in their FAQ:
Can I thenReturn() an inlined mock()?
Unfortunately you cannot do this:
when(m.foo()).thenReturn(mock(Foo.class));
// ^
The reason is that detecting unfinished stubbing wouldn't work if we allow above construct. We consider is as a 'trade off' of framework validation (see also previous FAQ entry). However you can slightly change the code to make it working:
//extract local variable and start smiling:
Foo foo = mock(Foo.class);
when(m.foo()).thenReturn(foo);
The workaround, as mentioned, is to store the desired returned value in a local variable, like you have done.
The way I understand it is that Mockito validates the usage you make of it every time you call its methods. When another method is called during an on-going stubbing process, you are breaking its validation process.
You can't use a method in thenReturn, but you can in thenAnswer
Your code will be called after the when condition will occur,
unlike any workaround based on thenReturn
Thus you could write:
#Test
public void nowWorkingTest() {
Mockito.when(mockedFirstObject.getSecondObject()).thenAnswer(new Answer<Map>() {
#Override
public Map answer(InvocationOnMock invocation) {
return setupMockedSecondObject();
}
});
Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}
Let find another example here
#Test
public void testAuthenticate_ValidCredentials() throws FailedToAuthenticateException {
String username = "User1";
String password = "Password";
/*Configure Returning True with when...thenReturn configuration on mock Object - Q5*/
//Write your code here
assertTrue(authenticator.authenticateUser(username, password));
}
I have a void method "functionVoid" that informs a parameter.
public class MyMotherClass {
#Inject
MyClass2 myClass2
public String motherFunction(){
....
String test = "";
myClass2.functionVoid(test);
if (test.equals("")) {
IllegalArgumentException ile = new IllegalArgumentException(
"Argument is not valid");
logger.throwing(ile);
throw ile;
}
....
}
}
public class MyClass2 {
public void functionVoid(String output_value)
{ ....
output_value = "test";
....
}
}
How do I mock this method in the JUnit method my method "motherFunction"?
In my example, the "test" variable is still empty.
#RunWith(MockitoJUnitRunner.class)
public class MyMotherClassTest {
#Mock
private MyClass2 myClass2 ;
#InjectMock
private final MyMotherClass myMotherClass = new MyMotherClass ();
#Test
public void test(){
myMotherClass.motherFunction();
}
}
If you want to mock the return result of motherFunction then you need not worry about the internal implementation of the method (which ends up calling functionVoid). What you do need to do is provide Mockito with an instruction as to what to do when the method, motherFunction is invoked, this can be achieved via the when clause with syntax;
when(mockedObject.motherFunction()).thenReturn("Any old string");
If that misses the point of what you are attempting to achieve then look at how to mock void methods in the documentation and determine whether the use of doAnswer is applicable here, something like;
doAnswer(new Answer<Void>() {
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
String output_value = invocation.getArguments()[0];
output_value = "Not blank";
return null;
}
}).when(myClass2).functionVoid(anyString());
If you can change functionVoid() to accept a mutable object as the parameter, then you should be able to achieve what you want.
For example, if you change functionVoid() as follows:
public void functionVoid(StringBuilder output_value)
{ ....
output_value.append("test");
....
}
and invoke it in your motherFunction as follows:
public String motherFunction(){
....
StringBuilder test = new StringBuilder();
myClass2.functionVoid(test);
if (test.toString().equals("")) {
Now modifying OceanLife's answer above, you should be able to do the following:
doAnswer(new Answer<Void>() {
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
StringBuilder output_value = invocation.getArguments()[0];
output_value.append("Not blank");
return null;
}
}).when(myClass2).functionVoid(any(StringBuilder.class));
Of course, if you can change functionVoid(), you could also just make it return a String instead of void.
In my example, the "test" variable is still empty.
This is not a Mockito problem.
Take a look at this question and especially this answer.
The gist of it is that Java is pass by value (this is explained far better at the links above). Nothing in Mockito or Java will ever be able to make the test var anything other than an empty String. It's an empty String before the method call, and will be an empty String after the call.
You can change an object's state within a method (e.g. adding objects to a collection within a method) and see those changes when you exit the method, but you cannot change what object a var references within a method and expect those changes to "stick" once you exit the method. Strings however, are effectively immutable (no state to change), so you can't even do this.
Thus no modifications to test can be made within that method call.
If you want to check method someMethod(String arg) of object Obj then:
String argument = "";
Mockito.verify(Obj, Mockito.times(1)).someMethod(argument);
Obj has to be Mock or Spy.
This works when you want to check if proper argument was passed to void method.
If your method modifies somehow argument then you should use assertion:
someMethod(StringWrapper wrapper) that changes string.
// given
String argument = "a";
String expected = "a_changed";
String wrapped = new StringWrapper(a);
// when
someMethod(wrapped);
// then
Assert.assertEquals(wrapped.getString(), expected)
I am not sure if this what you were looking for?