0
votes

According to these links: https://stackoverflow.com/a/20056622/1623597 https://stackoverflow.com/a/15640575/1623597 TestNG doesn't create new instance on every method test.

I have spring boot application. I need to write integration tests (Controller, service, Repositories). Sometimes to create new test case I need some entities in DB. To forget about any predefined entities in db, I decided to mock repository layer. I've just implemented ApplicationContextInitializer that finds all JPA Repositoryies in classpath and adds theirs mocks to spring context.

I got new problem that my mocks are created once per one ControllerTest (that extends AbstractTestNGSpringContextTests). Tested context creates only once, and mock instances are the same for all methods. Now, I have

//All repos are mocked via implementation of ApplicationContextInitializer<GenericWebApplicationContext>
// and added to spring context by
//applicationContext.getBeanFactory().registerSingleton(beanName, mock(beanClass));   //beanClass in our case is StudentRepository.class
@Autowired
StudentRepository studentRepository;

//real MyService implementation with autowired studentRepository mock
@Autowired
MyService mySevice;

@Test
public void test1() throws Exception {
    mySevice.execute();     //it internally calls studentRepository.findOne(..); only one time
    verify(studentRepository).findOne(notNull(String.class));
}

//I want that studentRepository that autowired to mySevice was recreated(reset)
@Test
public void test2() throws Exception {
    mySevice.execute();     //it internally calls studentRepository.findOne(..); only one time
    verify(studentRepository, times(2)).findOne(notNull(String.class)); //I don't want to use times(2)
    //times(2) because studentRepository has already been invoked in test1() method

}

@Test
public void test3() throws Exception {
    mySevice.execute();     //it internally calls studentRepository.findOne(..); only one time
    verify(studentRepository, times(3)).findOne(notNull(String.class)); //I don't want to use times(3)
}

I need to increase times(N) for every next method.I understand that it's testng implementation, but I try to find good solution for me. For my services I use constructor autowiring and all fields are final.

Questions:

  1. Is it possible to force testng to create new instance for every method test? Can I recreate spring context for every method test?

  2. Can I create my custom proxy for every mocked repository and reset mocks in @BeforeMethod method via my proxy?

1
Did you already try to reset what you want in a @Before/AfterMethod? - juherr
I can't use this approach. The reason is that I could have 10 repositories, that are used in another 20 services. every of this service uses constructor autowiring and has final fields. Moreover I can't pass over all services and replace mocks in @Before/AfterMethod - Geniy
How do you initialize myService ? - juherr
It's spring boot application. I've just added @_Service annotation. This service has @_Autowired before constructor (to autowire repos and so on) - Geniy
Could you share the full test class ? - juherr

1 Answers

0
votes

Actually I didn't need to create new repository mock instances in context, I just needed to reset their state. I thought that Mockito.reset(mock) just create new instance and assign it to reference mock so far. But it isn't true. The real behavior of Mockito.reset is just clean current state for mock without creating new mock instance.

My solution:

import org.springframework.data.repository.Repository;

@Autowired
private List<Repository> mockedRepositories;

@BeforeMethod
public void before() {
    mockedRepositories.forEach(Mockito::reset);
}

This code autowire all repos that were mocked in ApplicationContextInitializer, and reset their state. Now I can use verify() without times(2)

@Test
public void test2() throws Exception {
    mySevice.execute()
    verify(studentRepository).findOne(notNull(String.class));
}