3
votes

I am new to Mockito. I am trying to write jnuit for a service by mocking the db interactions:

I have following classes (just representative of actual classes)

public class TestService{

    public Response getTestList(String type){
        list = getListbyType(type);

        return response.entity(list);
    }

    private List getListbyType(String type){
          ...
          ..
          TestDAO testdao = new Testdao(sqlconn);
          list = testdao.getListfromDB(type)
          return list;
   }
}

And my test class is something like

public class TestServiceTest{

    @InjectMocks
    private TestService testService = new TestService();
    @Test
    public void getTestListTest(){

        List testlist = new List();
        tetslist.add("test1");

        TestDAO testdaomock = mock(TestDAO);
        when(testdaomock.getListfromDB("test")).thenreturn(list);

        list = testService.getTestList(test);
    }
}

But when I run this test, it still invokes the actual DB call and retrieves the values from db and not the mocked values, should I mock sql connection and non default constructor? I am clueless.

-- UPDATE

As people have suggested, I moved DAO instantiation to my service constructor and also used Spy, But still my actual DB call is invoked instead of the mock call.

2
testdaomock.getListfromDB("test") should not compile because private List getListbyType(String type) is private. How does it compile? - user3458
getListbyType(String type) is private, but testdaomock.getList‌​fromDB("test") is in DAO class which is public - user1933888
@RunWith(MockitoJUnitRunner.class) - WildDev
Possible duplicate of Test class with a new() call in it with Mockito - user3458
The problem of this test is actually poor design of the code under test: one should not instantiate any DAO within the call to a "getter". The only place for that DAO instance is to be a field, instantiated at construction or lazily when required (the test would do the former). - Oleg Sklyar

2 Answers

2
votes

Your problem is with the following statement:

TestDAO testdao = new Testdao(sqlconn);

The instance of TestDAO you got from mock() is not the instance used in testdao.getListfromDB(type) that follows new

In order to successfully mock, you need to apply the inversion of dependencies throughout. That means new must only show up in classes you don't intend to test, such as simplistic factories or Spring config files.

[Update]

If proper IOC is not an option, you can introduce a method that allocates DAO and then spy() it. The method would have to be package-private. This is described here.

Another option would be to add a package-private constructor taking testdao as argument and expressing your default constructor in the terms of that constructor.

2
votes

In case you can't change the TestService class (legacy code) there is a possiblity to mock the new instance with PowerMockito (https://github.com/jayway/powermock). This uses a own junit runner (PowerMockRunner) that allows the byte-code manipulation.

Here an example for your code:

import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;
import org.mockito.Mock;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
public class TestServiceTest {

   @Mock
   TestDAO testDaoMock;

   @Test
   public void test() throws Exception {
    List<String> testlist = new ArrayList<>();
    testlist.add("test1");
    when(testDaoMock.getListfromDB(anyString())).thenReturn(testlist);
    PowerMockito.whenNew(TestDAO.class).withAnyArguments().thenReturn(testDaoMock);
    TestService testService = new TestService();
    Response actualResponse = testService.getTestList("testType");
    assertEquals(expectedResponse, actualResponse);
    }
}

You will need to add powermock-api-mockito and powermock-module-junit4 to your dependcies.