在 Autowired 服务类中测试 REST API 时,如何使用测试 application.yml 覆盖主 application.yml?
Posted
技术标签:
【中文标题】在 Autowired 服务类中测试 REST API 时,如何使用测试 application.yml 覆盖主 application.yml?【英文标题】:How to override main application.yml with testing application.yml when testing REST API in an Autowired service class? 【发布时间】:2019-12-17 05:15:13 【问题描述】:我正在使用 TestNG 为我的应用程序的 REST API 编写自动化测试。该应用程序有一个 RestController,其中包含一个 @Autowired 服务类。当使用 HTTP GET 请求调用 REST 端点时,服务会查找 XML 文件的存储目录,将其内容转换为对象并将它们存储在数据库中。对于我的问题,重要的是存储目录的路径存储在 /src/main/resources/application.yml (source.storage) 中,并通过 @Value 注释导入。
现在,我在 src/test/resources/application.yml 中也有 source.storage 属性,它指向 src/test 中的另一个目录,我在其中存储我的测试 XML 文件,并将它们导入到我的测试类中@Value 再次注解。我的测试使用 HTTP GET 调用 REST 端点。但是,该服务似乎仍将 source.storage 属性绘制为主 application.yml,而我希望该值被测试 application.yml 文件中的值覆盖。换句话说,该服务尝试从应用程序存储目录中导入 XML 文件,而不是从我的测试存储中。
@ActiveProfiles 和 @TestPropertySource 似乎对我不起作用。扫描主 application.yml 的存储属性不是一种选择,因为最终 application.yml 将从 Spring Cloud Config 中提取,我不知道主 application.yml 的位置。
有没有一种方法可以让@Autowired 服务从测试 application.yml 中提取 source.storage 属性,而不是从主要的?
任何建议将不胜感激。
谢谢,彼得
【问题讨论】:
【参考方案1】:嗯,这实际上取决于您要构建的内容,是控制器的某种单元测试还是更可能是集成测试。这两种方法都在 tutorial 中进行了解释。
如果您尝试编写 集成测试,从您的问题来看,这似乎更有可能,那么 @ActiveProfiles
或 @TestPropertySource
应该适合您。我建议使用配置文件,在具有很多属性的不断增长的应用程序中,只需替换一些属性进行测试会更方便一些。以下是在为控制器端点编写集成测试时对我有用的设置:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class AreaControllerTest
@Autowired
TestRestTemplate rest;
@MockBean
private JobExecutor jobExecutor;
@Test
public void test01_List()
//
@Test
public void test02_Get()
//
// ...
有几件重要的事情。
测试属性位于src/test/resources/application-test.properties
中,并与application.properties
中的属性合并,正如@ActiveProfiles("test")
注释所暗示的那样。
Essential 也是 @RunWith(SpringRunner.class)
,它是特定于 JUnit 的,有关 TestNG 替代方案,请参阅 this SO question。
最后,@SpringBootTest
注释将启动整个应用程序上下文。
@FixMethodOrder
和 @DirtiesContext
是测试用例的进一步设置,并不是真正需要的。
还要注意@MockBean
注释,在这种情况下,我们不想使用JobExecutor
的实际实现,因此我们将其替换为模拟。
如果你想编写单元测试,你只想自己检查控制器和服务的逻辑,那么你必须有两个测试类,每个测试各自的类。测试服务应该是标准的单元测试,测试控制器有点棘手,可能更倾向于部分集成测试。如果这是您的情况,我建议使用上述教程中解释的MockMvc
方法。来自那里的小sn-p:
@RunWith(SpringRunner.class)
@WebMvcTest(GreetingController.class)
public class WebMockTest
@Autowired
private MockMvc mockMvc;
@MockBean
private GreetingService service;
@Test
public void greetingShouldReturnMessageFromService() throws Exception
when(service.greet()).thenReturn("Hello Mock");
this.mockMvc.perform(get("/greeting")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello Mock")));
注意 @MockBean
注释,它模拟服务,您可以在其中指定自己的模拟行为。这一点很关键,因为这种测试不会加载整个应用程序上下文,而只会加载 MVC 上下文,因此服务不可用。同样,在集成测试中,@RunWith(SpringRunner.class)
注释是必不可少的。最后@WebMvcTest(GreetingController.class)
只启动GreetingController
类的MVC 上下文,而不是整个应用程序。
【讨论】:
【参考方案2】:您可以尝试将属性直接提供给 Spring Boot 测试。
@SpringBootTest(properties= "source.storage=someValue")
关于获取错误属性源的应用程序,您还应该检查您的应用程序是否正在正确构建。
【讨论】:
以上是关于在 Autowired 服务类中测试 REST API 时,如何使用测试 application.yml 覆盖主 application.yml?的主要内容,如果未能解决你的问题,请参考以下文章
单元测试中的 Spring Boot @Autowired 返回 NullPointerException
NUnit 5 Spring MVC 测试 NoSuchBeanDefinitionException 用于子模块中的 Autowired 依赖项
如何为同一服务使用@Autowired 注解两个或多个不同的组件类?
Junit问题01 利用 @Autowired 注入失效问题