繁体   English   中英

Mockito 和 Javax 注释 @PostConstruct

[英]Mockito and Javax annotation @PostConstruct

我正在使用 JUnit Jupiter 为一个类编写 Java 单元测试,该类具有带有javax.annotation.PostConstruct注释的方法。

我想测试类中的一个方法,但遇到了一个问题,即一旦创建对象,@ @PostConstruct方法总是被调用(不出所料)。 我创建的用于@PostConstruct方法的 Mockito 模拟对象没有提供我需要的结果。

这是创建对象时调用的方法:

@Service
@Slf4j
public class MyService {
    @Inject
    private MyRepository myRepository;

    private Integer myId;

    @PostConstruct
    public void postInit() {
        MyObject myObj = myRepository.findByName("FOO");
        myId = myObj.getId();
    }

    public void myMethod() {
        ... code to be tested here...
    }
}

我的 JUnit 测试类包含以下内容:

@ExtendWith(SpringExtension.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class MyServiceTest {
    private static final Integer ID = 0;

    @Test
    public void myTest() {
        // Given.
        MyObject myObj = new MyObject();
        myObj.setId(ID);

        MyRepository myRepository = Mockito.mock(MyRepository.class);
        Mockito.when(myRepository.findByName(anyString())).thenReturn(myObj);

        // Test runs here.
        myService.myMethod()
    }
}

当我运行单元测试时,我可以看到模拟已通过我的 IDE 实例化。 然而,当调用findByName("FOO")方法时,它总是返回一个 NULL 对象,即使它应该返回一个正确的MyObject实例。

因此,单元测试失败并返回 NullPointerException,因为myObj为 null。

我在这里做的有什么明显不正确的>?

在此先感谢您的帮助。

我稍微修改了您的测试,通过Mockito创建MyService并使myMethod返回 id:

@ExtendWith(SpringExtension.class)
class MyServiceTest {
    private static final Integer ID = 42;

    @InjectMocks
    MyService myService;

    @Test
    public void myTest() {
        // Given.
        MyObject myObj = new MyObject();
        myObj.setId(ID);

        MyRepository myRepository = Mockito.mock(MyRepository.class);
        Mockito.when(myRepository.findByName(anyString())).thenReturn(myObj);

        // Test runs here.
        assertThat(myService.myMethod()).isEqualTo(ID);
    }
}

当我运行这个时,我得到:

java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because "this.myId" is null

    at MyService.myMethod(MyService.java:24)
    at MyServiceTest.myTest(MyServiceTest.java:28)
    ...

很明显,我们对findByName的嘲笑没有奏效。

那是因为myRepository在我们的测试中只是一个局部变量——没有什么可以将它注入MyService

解决此问题的一种方法是不使用 spring 测试基础架构,而只是使用普通的 Mockito。 我们使用标准的@Mock/@InjectMocks 注释来创建我们的模拟和我们正在测试的对象。 这里的缺点是我们必须记住自己调用postInit

@ExtendWith(MockitoExtension.class)
class MyServiceTest {
    private static final Integer ID = 42;

    @Mock
    MyRepository myRepository;

    @InjectMocks
    MyService myService;

    @Test
    public void myTest() {
        // Given.
        MyObject myObj = new MyObject();
        myObj.setId(ID);

        Mockito.when(myRepository.findByName(anyString())).thenReturn(myObj);
        myService.postInit();
        // Test runs here.
        assertThat(myService.myMethod()).isEqualTo(ID);
    }
}

要使用 Spring 调用 postInit,您可以这样做:(我不确定是否有更惯用的方法来创建模拟 MyRepository 实例)

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {MyService.class})
@ContextConfiguration(classes = {MyServiceTest.Configuration.class})
class MyServiceTest {
    @TestConfiguration
    static class Configuration {
        @Bean
        public MyRepository myRepository() {
            MyRepository myRepository = mock(MyRepository.class);
            MyObject myObj = new MyObject();
            myObj.setId(ID);
            Mockito.when(myRepository.findByName(anyString())).thenReturn(myObj);
            return myRepository;
        }
    }
    private static final Integer ID = 42;
    
    @Autowired
    MyService myService;
    
    @Test
    public void myTest() {
        assertThat(myService.myMethod()).isEqualTo(ID);
    }
}

避免使用@SpringBootTest一种方法是在我们的测试配置中创建MyService实例(我也将其转换为构造函数注入):

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {MyServiceTest.Configuration.class})
class MyServiceTest {
    @TestConfiguration
    static class Configuration {
        @Bean
        public MyRepository myRepository() {
            MyRepository myRepository = mock(MyRepository.class);
            MyObject myObj = new MyObject();
            myObj.setId(ID);
            Mockito.when(myRepository.findByName(anyString())).thenReturn(myObj);
            return myRepository;
        }

        @Bean
        public MyService myService(MyRepository myRepository) {
            return new MyService(myRepository);
        }
    }
    private static final Integer ID = 42;

    @Autowired
    MyService myService;

    @Test
    public void myTest() {
        assertThat(myService.myMethod()).isEqualTo(ID);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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