简体   繁体   中英

unit testing mock injection

I am trying to unit test my class, and mock the DAO to provide predictable results. Still, it would appear that my DAO's method is still being called as I receive a hibernate error.

org.hibernate.exception.GenericJDBCException: Station TST not found.

This error is a result of my database not containing TST, but shouldnt this not even be called since I mocked the DAO? How do I mock the call so that the database isn't even hit.

Here's how I set up my mock

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class MyServiceTest{

    @Autowired
    private MyService service;

    private MyDAO dao;

    private LinkedList objs;    


    @Before
    public void init() throws SecurityException, NoSuchFieldException,
            IllegalArgumentException, IllegalAccessException {


        // mock the dao for predictable results
        dao = mock(MyDAO.class);

        when(dao.myDBCall(anyString(), any(Date.class))).thenReturn(legs);
        Field f = MyService.class.getDeclaredField("dao");
        f.setAccessible(true);
        f.set(service, dao);


    }

    @Test
    public void testCall() {
        // construct our sample leg data
        legs = new LinkedList();
//fill in my stub data i want to return

        Collections.shuffle(legs);

        List results = service.testingCall("TST", new Date()); // this fails because service is using the dao, but it is making a call to the DB with this garbage 'Test' param rather than just returning 'legs' as stated in my when clause
        assertThat(results, not(nullValue()));


    }

    @Test
    public void testGetGates() {
        // fail("Not yet implemented");
    }

    @Test
    public void testGetStations() {
        // fail("Not yet implemented");
    }
}

I think that your Service is instantiated by spring with your Dao defined in your application-context.xml and you get your error before you even try to inject your mock in your service.

One thing I like to use to test my services is an Autowired constructor and then in my tests I do not instantiate my services by spring but using the constructor and the mock.

private MyDao myDao;

@Autowired
public MyService(MyDao myDao){
this.myDao = myDao;
}

And then in your test :

MyService myService = new Myservice(mockedDao);

For first, instead of Field f = MyService.class.getDeclaredField("dao"); check for PowerMock (check for Whitebox )

Mocking one object will not prevent hibernate to start (because you are loading applicationContext.xml , so hibernate will try to connect to DB). Your mock - it is just a POJO at one field of one test instance, not a Spring bean or replacement for class.

If you want to test only dao and service classes (create clear unit test) - remove Spring related annotations from test and do it on your own (create mock, create service, put mock to service, and test it).

If you want test your application with Spring context (create integration test) - create H2 in-memory database for hibernate and just test your service (don't forget to clear it in @After ).

Third way - split your configuration file into two Spring profiles (for example dev and test ) and implement mock yourself (put mock to test , real hibernate and dao to dev ).

If you dont want use Spring profiles - it is possible to split applicationContext.xml into 3 files (for common beans, for real DB beans, for mocks).

And one more sexy way - use springockito-annotations (but you still need to avoid hibernate be loaded)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM