0

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

Rohit Singh
  • 16,950
  • 7
  • 90
  • 88

3 Answers3

2

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.

Arnold Schrijver
  • 3,588
  • 3
  • 36
  • 65
  • i did not understand . Are you asking or telling the solution ? – Rohit Singh Aug 06 '17 at 05:44
  • Its an answer. Once you mocked the class, it is not the real thing (object) anymore. So the implementation code inside `StringChecker` is not as well. You set a mocked return value on `getSecretKey`, but not on `isEqual`. – Arnold Schrijver Aug 06 '17 at 05:46
  • If you would like to test like in your example you could use mockito `spy`, where you selectively mock only part of a class and keep the remainder 'real'. – Arnold Schrijver Aug 06 '17 at 05:48
  • i was assuming since i have used when().then return . it will give me the String value when it will be called inside mocked class . Am i wrong here ? – Rohit Singh Aug 06 '17 at 05:56
  • Yes, that works, but `stringChecker.isEqual()` has no implementation code in the mock, so calling it without setting it makes no sense (and in this case setting it, then testing it makes no sense at well :) – Arnold Schrijver Aug 06 '17 at 05:58
  • How can i do that can you please add in your answer ? – Rohit Singh Aug 06 '17 at 05:59
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/151178/discussion-between-arnold-schrijver-and-rohit-singh). – Arnold Schrijver Aug 06 '17 at 06:00
  • PS If this is real code and security is important, don't store your secret key like this.. – Arnold Schrijver Aug 06 '17 at 06:21
1

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

Rohit Singh
  • 16,950
  • 7
  • 90
  • 88
0

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);
}
Dmytro Maslenko
  • 2,247
  • 9
  • 16