.properties文件中的Spring Boot单元测试@Value提供了NullPointerException

时间:2019-09-23 12:36:58

标签: spring-boot junit spring-boot-test spring-junit

我正在尝试从属性文件中读取Spring Boot中单元测试用例的值。我有两个config.properties文件,其中一个src/main/resources

prop = some-value

src/test/resources中的一个:

prop = some-test-value

主应用程序类:

package company.division.project;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.PropertySource;

@SpringBootApplication(scanBasePackages = "company.division.project")
@PropertySource(value = "classpath:config.properties")
public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        System.setProperty("DUMMY_PROPERTY", "dummy-value");

        return application.sources(Application.class);
    }

    public static void main(String[] args) throws Exception {
        // Do nothing with main
    }
}

要测试的服务类别:

package company.division.project.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

@Component
public class Service {
    @Autowired
    Environment environment;

    public String getProperty() {
        return environment.getProperty("prop");
    }

}

ServiceTest类。我尝试了两种方法来检索src/test/resources/config.properties文件中的值;一个带有@Autowired Environment,另一个带有@Value注解...均无效:

package company.division.project.service;

import static org.junit.Assert.assertEquals;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.test.context.TestPropertySource;

@RunWith(MockitoJUnitRunner.class)
@TestPropertySource("classpath:config.properties")
public class ServiceTest {
    @InjectMocks
    Service service;

    @Autowired
    Environment environment;

    @Value("${prop}")
    private String expectedProperty;

    @Test
    public void testGetPropertyWithValueAnnotation() {
        assertEquals(expectedProperty, service.getProperty());
    }

    @Test
    public void testGetPropertyWithEnvironment() {
        assertEquals(environment.getProperty("prop"), service.getProperty());
    }
}

我在StackOverflow上的某个地方读到,为了自动连接Spring测试类中的组件,我需要为测试创建整个上下文,因此我尝试了这一点(更改批注和测试运行器):< / p>

package company.division.project.service;

import static org.junit.Assert.assertEquals;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.test.context.junit4.SpringRunner;


@RunWith(SpringRunner.class)
@SpringBootTest
public class ServiceTest {
    @InjectMocks
    Service service;

    @Autowired
    Environment environment;

    @Value("${prop}")
    private String expectedProperty;

    @Test
    public void testGetPropertyWithValueAnnotation() {
        assertEquals(expectedProperty, service.getProperty());
    }

    @Test
    public void testGetPropertyWithEnvironment() {
        assertEquals(environment.getProperty("prop"), service.getProperty());
    }
}

创建了上下文,但是两种方法都在NullPointerException s中再次结束。

2 个答案:

答案 0 :(得分:1)

测试的问题是您试图以错误的方式使用MockitoJUnitRunner.class

如果您正在使用@InjectMocks模拟服务,则需要确保您需要通过模拟服务调用来返回值Service.getProperty()。如果您使用的是SpringRunner.class,那么您应该没有@InjectMocks,但应该有@Autowired用于服务。跟踪测试工作。

@RunWith(SpringRunner.class)
@SpringBootTest
public class ServiceTest {
    @Autowired
    Service service;

    @Autowired
    Environment environment;

    @Value("${prop}")
    private String expectedProperty;

    @Test
    public void testGetPropertyWithValueAnnotation() {
        assertEquals(expectedProperty, service.getProperty());
    }

    @Test
    public void testGetPropertyWithEnvironment() {
        assertEquals(environment.getProperty("prop"), service.getProperty());
    }
}

答案 1 :(得分:1)

由于@shazin的回​​答以及我自己的一些研究,我才得以解决该问题。

基本上,@RunWith中指定的测试运行器类与Mockito模拟的注释之间必须具有兼容性。我们要测试Service类:

服务等级

@Component
public class Service {
    @Autowired
    Environment environment;

    public String getProperty() {
        return environment.getProperty("prop");
    }
}

如果您使用的是@RunWith(MockitoJUnitRunner.class),则可以使用@InjectMocks@Mock注释,如下所示。 @Autowired中的Service是什么,都将自动与模拟连线:

使用MockitoJUnitRunner 的测试类:

@RunWith(MockitoJUnitRunner.class)
public class ServiceTest {
    @InjectMocks
    Service service;
        @Mock
        Environment mockEnvironment;

    @Before
    public void before() {
        Mockito.when(mockEnvironment.getProperty("prop")).thenReturn("some-test-value")
    }
}

但是您不能自动连接测试类中的任何东西 。这需要一个Spring Context(需要一个Spring Context来管理自动连接到对象中的bean)。这就是@RunWith(SpringRunner.class)进入图片的地方。您可以使用它在专用的Spring上下文中运行测试用例(您会注意到测试用例日志显示了每个带有@RunWith(SpringRunner.class)注释的测试类都在启动的新Spring应用程序)。您还需要提供带有@SpringBootTest批注的“配置”详细信息。

需要注意的是,带有@RunWith(SpringRunner.class)的测试类将无法理解@InjectMocks@Mock注释;您将必须使用@MockBean批注。通过用它们的模拟替换bean,这将有效地修改Spring上下文。带有@Autowired批注的所有内容都会自动与模拟bean自动关联:

使用SpringRunner 的测试类:

@RunWith(SpringRunner.class)
@SpringBootTest(classes=Application.class)
public class ServiceTest {
    @Autowired
    Service service;

    @MockBean
    Environment mockEnvironment;

    @Before
    public void before() {
        Mockito.when(mockEnvironment.getProperty("prop")).thenReturn("some-test-value")
    }
}

因此...使用@RunWith(SpringRunner.class)除了更改注释的名称({{1}-> @InjectMocks@Autowired-> {{ 1}}),对吗?错误。使用@Mock使您可以在测试用例中自动接线 。因此,如果您想使用实际的@MockBean(而不是模拟的),也可以这样做。只需从专用的Spring上下文中自动将其插入:

SpringRunnerEnvironment环境下测试类

SpringRunner

这解决了问题。