简体   繁体   中英

Spring integration testing with mocked dependencies

I'm using Spring Boot. I have Service class (annotated @Service ) with a method annotated with @Retryable .

For one of my integration test, I would like to load the Service bean to the context (with all of it's configurations and annotations).

The service has several dependencies. For example,

@Service
@EnableRetry
public class MyService{
    private final BeanOne beanOne;
    private final BeanTwo beanTwo;

    public MyService(BeanOne beanOne, BeanTwo beanTwo){
        this.beanOne = beanOne;
        this.beanTwo = beanTwo
    }
    
    @Retryable(value = RuntimeException.class , maxAttempts = 3)
    public void serviceAction(){
        beanOne.doSomething();
        beanTwo.doSomething();
        doMoreThings();
    }

    private void doMoreThings(){
        //things
        //eventually throws Runtime Exception

    }
}

I want to check that I indeed implemented everything regarding the retry correctly.

I'm trying to write a Spring integration test, when mocking the dependency beans, but still loading the MyService bean to the context (to check the retryable).

I tried mocking the dependency beans (using JUnit4 and Mockito):

@Import({MyService.class})
@RunWith(SpringRunner.class)
public class MyServiceIntegrationTest{
    @Autowired
    private MyService myService;
    
    @MockBean
    private BeanOne beanOne;

    @MockBean
    private BeanTwo beanTwo;

    @Test
    public void test(){
        myService.serviceAction();
        verify(beanTwo,times(3)).doSomething();
    }
}

However, I'm getting NullPointerException on the beanOne.doSomething() call. It seems like beanOne and beanTwo fields in MyServiceIntegrationTest class are indeed mocked, but are not autowired to the actual myService , and instead myService 's fields are null. Note that the @Retryable annotation indeed works (and repeats 3 times), however this is since NullPointerException is a RuntimeException. The code never actually reaches the doMoreThings() method.

@Import is used to load configurations you should use @SpringApplicationConfiguration or even better the newer @SpringBootTest annotation. @SpringApplicationConfiguration is currently deprecated by the way.

example:

@SpringBootTest(classes = {MyService.class}) // or @SpringApplicationConfiguration with the same paramters
@RunWith(SpringRunner.class)
public class MyServiceIntegrationTest{
    @Autowired
    private MyService myService;
    
    @MockBean
    private BeanOne beanOne;

    @MockBean
    private BeanTwo beanTwo;

    @Test
    public void test(){
        myService.serviceAction();
        verify(beanTwo,times(3)).doSomething();
    }
}

check:

Found the cause - when running the whole testing suite, it was being ran in parallel. In Spring Integration testing there are some shared aspects of the context, and thus it caused some errors. The solution was to add for each testing class like the above the annotation: @DirtiesContext(classMode=ClassMode.BEFORE_EACH_TEST_METHOD)

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