Spring 来自 .properties 文件的引导单元测试 @Value 给出 NullPointerException
Spring Boot Unit Test @Value from .properties File gives NullPointerException
我正在尝试从 Spring Boot 中的单元测试用例的属性文件中读取值。我有两个 config.properties
文件,一个在 src/main/resources
:
prop = some-value
和 src/test/resources
中的一个:
prop = some-test-value
主应用程序class:
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
}
}
待测服务class:
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");
}
}
服务测试class。我尝试了两种方法来检索 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());
}
}
我在 Whosebug 的某个地方读到,为了在 Spring 测试 class 中自动连接组件,我需要为测试创建一个完整的上下文,所以我尝试了这个(更改注释和测试运行器):
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
秒结束。
您的测试存在的问题是您试图以错误的方式使用 MockitoJUnitRunner.class
。
如果您使用 @InjectMocks
模拟服务,您需要确保需要通过模拟服务调用来 return 值 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());
}
}
感谢@shazin 的回答和我自己的一些研究,我已经能够解决这个问题。
基本上,@RunWith
中指定的测试 运行ner class 与 Mockito 模拟的注释之间需要兼容。我们要测试 Service
class:
服务Class:
@Component
public class Service {
@Autowired
Environment environment;
public String getProperty() {
return environment.getProperty("prop");
}
}
如果您使用的是 @RunWith(MockitoJUnitRunner.class)
,则可以使用如下所示的 @InjectMocks
和 @Mock
注释。 Service
中的 @Autowired
将自动连接到模拟:
用MockitoJUnitRunner
测试Class:
@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")
}
}
但是您无法在测试 class 本身 中自动连接任何东西 。这需要一个 Spring 上下文(需要一个 Spring 上下文来管理自动连接到对象中的 bean)。这就是 @RunWith(SpringRunner.class)
发挥作用的地方。您可以将它用于 运行 具有专用 Spring 上下文的测试用例(您会注意到测试用例日志显示每个测试都会启动一个新的 Spring 应用程序 class 与 @RunWith(SpringRunner.class)
注释)。您还需要提供带有 @SpringBootTest
注释的配置详细信息。
需要注意的是,带有 @RunWith(SpringRunner.class)
的测试 class 无法理解 @InjectMocks
和 @Mock
注释;您必须使用 @MockBean
注释。这将通过用它们的模拟替换 bean 来有效地修改 Spring 上下文;任何带有 @Autowired
注释的东西都将自动与模拟 bean 自动连接:
用SpringRunner
测试Class:
@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)
除了更改注释的名称(@InjectMocks
-> @Autowired
和 @Mock
-> @MockBean
),对吧?错误的。使用 SpringRunner
可以让您在测试用例 中自动装配组件 。因此,如果您想使用实际的 Environment
(不是模拟的),您也可以这样做;只需从专用的 Spring 上下文自动连接它:
使用 SpringRunner
和 @Autowired
环境:
测试 Class
@RunWith(SpringRunner.class)
@SpringBootTest(classes=Application.class)
public class ServiceTest {
@Autowired
Service service;
@Autowired
Environment environment;
@Test
public void testServiceGetProperty() {
assertEquals(environment.getProperty("prop"), service.getProperty("prop");
}
}
这就解决了问题。
我正在尝试从 Spring Boot 中的单元测试用例的属性文件中读取值。我有两个 config.properties
文件,一个在 src/main/resources
:
prop = some-value
和 src/test/resources
中的一个:
prop = some-test-value
主应用程序class:
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
}
}
待测服务class:
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");
}
}
服务测试class。我尝试了两种方法来检索 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());
}
}
我在 Whosebug 的某个地方读到,为了在 Spring 测试 class 中自动连接组件,我需要为测试创建一个完整的上下文,所以我尝试了这个(更改注释和测试运行器):
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
秒结束。
您的测试存在的问题是您试图以错误的方式使用 MockitoJUnitRunner.class
。
如果您使用 @InjectMocks
模拟服务,您需要确保需要通过模拟服务调用来 return 值 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());
}
}
感谢@shazin 的回答和我自己的一些研究,我已经能够解决这个问题。
基本上,@RunWith
中指定的测试 运行ner class 与 Mockito 模拟的注释之间需要兼容。我们要测试 Service
class:
服务Class:
@Component
public class Service {
@Autowired
Environment environment;
public String getProperty() {
return environment.getProperty("prop");
}
}
如果您使用的是 @RunWith(MockitoJUnitRunner.class)
,则可以使用如下所示的 @InjectMocks
和 @Mock
注释。 Service
中的 @Autowired
将自动连接到模拟:
用MockitoJUnitRunner
测试Class:
@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")
}
}
但是您无法在测试 class 本身 中自动连接任何东西 。这需要一个 Spring 上下文(需要一个 Spring 上下文来管理自动连接到对象中的 bean)。这就是 @RunWith(SpringRunner.class)
发挥作用的地方。您可以将它用于 运行 具有专用 Spring 上下文的测试用例(您会注意到测试用例日志显示每个测试都会启动一个新的 Spring 应用程序 class 与 @RunWith(SpringRunner.class)
注释)。您还需要提供带有 @SpringBootTest
注释的配置详细信息。
需要注意的是,带有 @RunWith(SpringRunner.class)
的测试 class 无法理解 @InjectMocks
和 @Mock
注释;您必须使用 @MockBean
注释。这将通过用它们的模拟替换 bean 来有效地修改 Spring 上下文;任何带有 @Autowired
注释的东西都将自动与模拟 bean 自动连接:
用SpringRunner
测试Class:
@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)
除了更改注释的名称(@InjectMocks
-> @Autowired
和 @Mock
-> @MockBean
),对吧?错误的。使用 SpringRunner
可以让您在测试用例 中自动装配组件 。因此,如果您想使用实际的 Environment
(不是模拟的),您也可以这样做;只需从专用的 Spring 上下文自动连接它:
使用 SpringRunner
和 @Autowired
环境:
@RunWith(SpringRunner.class)
@SpringBootTest(classes=Application.class)
public class ServiceTest {
@Autowired
Service service;
@Autowired
Environment environment;
@Test
public void testServiceGetProperty() {
assertEquals(environment.getProperty("prop"), service.getProperty("prop");
}
}
这就解决了问题。