如何为我的 Spring Boot 控制器执行单元测试?
Posted
技术标签:
【中文标题】如何为我的 Spring Boot 控制器执行单元测试?【英文标题】:How to perform unit tests for my spring boot controller? 【发布时间】:2018-06-03 14:24:25 【问题描述】:我是 TDD 的新手,目前我正在学习一些关于如何为我的 Spring Boot API 编写自动化测试的知识。
所以,我有一个应用程序可以从我的数据库中恢复一堆报告。
我尝试使用我正在阅读的书中的自定义测试。但是测试不起作用。
我总是尝试运行我的测试,但我遇到了一个异常:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
Caused by: org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 1 in XML document from class path resource [application.properties] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:398)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:335)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:303)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:187)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:223)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:194)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:258)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadBeanDefinitions(AbstractGenericContextLoader.java:257)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:124)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:107)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:243)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
... 25 more
Caused by: org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.
at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:204)
at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:178)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:400)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:327)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(XMLScanner.java:1471)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:963)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:602)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:112)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:532)
at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:888)
at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:824)
at java.xml/com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
at java.xml/com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:243)
at java.xml/com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:339)
at org.springframework.beans.factory.xml.DefaultDocumentLoader.loadDocument(DefaultDocumentLoader.java:77)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadDocument(XmlBeanDefinitionReader.java:428)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390)
... 38 more
这是我正在尝试运行的测试。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:application.properties")
@ActiveProfiles("local")
public class ReportControllerTest
@Autowired
private MockMvc mvc;
@Test
public void doesRecoverPercentageOfInfectedPatients() throws Exception
MvcResult result = mvc.perform(get("/api/report/infected").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andReturn();
assertThat(result.getResponse().getStatus()).equals(status().isOk());
关于如何正确运行此测试或我做错了什么的任何建议或建议?
更新:
主类
@SpringBootApplication
public class ReportApiApplication
public static void main(String[] args)
SpringApplication.run(ReportApiApplication.class, args);
我的 application.properties 如下所示:
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/zssn?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
【问题讨论】:
您能否提供有关您的项目设置的更多信息?是xml还是java配置?你见过这个问题吗? ***.com/questions/24776669/… @Karapapas 在我的情况下,我只是使用 application.properties 文件,我将所有数据用于访问我的数据库和配置到休眠框架。 你能粘贴你的属性文件吗?我认为该错误为您提供了有关该问题的很好的提示。Line 1 in XML document from class path resource [application.properties] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.
@Karapapas 我在问题的末尾添加了我的 application.properties。有什么问题吗?
属性文件看起来不错。您的项目中是否有任何 .xml 文件?也许您的 xml 文件的格式有问题。你用什么IDE?
【参考方案1】:
您应该如下更改 ReportControllerTest。需要删除@ContextConfiguration(locations="classpath:application.properties")
同时在你的@ActiveProfiles("local") 的资源目录下创建application.local.properties
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
@ActiveProfiles("local")
public class ReportControllerTest
@Autowired
private MockMvc mvc;
@Test
public void doesRecoverPercentageOfInfectedPatients() throws Exception
MvcResult result = mvc.perform(get("/api/report/infected").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andReturn();
assertThat(result.getResponse().getStatus()).equals(status().isOk());
【讨论】:
嗨@gaurav-srivastav 我尝试了你的建议,但我仍然遇到同样的问题。 您使用的是哪个版本的 Spring Boot? 我使用的是2.0.2版本。 Junit 版本?如果您使用 Junit4 或如果使用 Junit 5 则使用 SpringRunner 则使用 SpringExtension 您是否使用 XML 定义来定义 bean 或 application.properties?【参考方案2】:经过一些研究和有益的谈话后,我发现了问题所在。 所以,实际上我使用的是 Java 10、JUnit 4 和 mockito 2。 我的 API 工作正常。但是当我尝试构建工件时,我注意到一些 hibernate 类没有正确编译,这是因为 hibernate 还没有完全支持 Java 10。
我用这个作为我的论据,发现 JUnit 和 Mockito 也没有完全支持 java 10。 因此,我所做的只是将我的开发环境更改为 Java 8,这样我的测试就可以正常工作了。
这是我的一个测试的最终版本。我根据我在 cmets 上获得的所有建议做了一些更改。
@SpringBootTest
@RunWith(SpringRunner.class)
@AutoConfigureMockMvc
public class ReportControllerTest
@Autowired
private MockMvc mvc;
@Autowired
private WebApplicationContext ctx;
@Before
public void setUp()
this.mvc = MockMvcBuilders.webAppContextSetup(this.ctx).build();
@Test
public void doesRecoverPercentageOfInfectedPatients() throws Exception
mvc.perform(MockMvcRequestBuilders.get("/api/report/infected")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
【讨论】:
以上是关于如何为我的 Spring Boot 控制器执行单元测试?的主要内容,如果未能解决你的问题,请参考以下文章
如何为具有 Spring Security 配置的 Spring Boot API 编写单元测试
如何为 ZuulException 自定义 Spring Boot 控制器 API 响应