为什么组件扫描不适用于Spring Bootunit testing?
服务类FooServiceImpl
使用@Service aka @Component
进行注释,这使其有资格进行自动assembly。 为什么在unit testing期间没有拾取和自动assembly此类?
@Service public class FooServiceImpl implements FooService { @Override public String reverse(String bar) { return new StringBuilder(bar).reverse().toString(); } } @RunWith(SpringRunner.class) //@SpringBootTest public class FooServiceTest { @Autowired private FooService fooService; @Test public void reverseStringShouldReverseAnyString() { String reverse = fooService.reverse("hello"); assertThat(reverse).isEqualTo("olleh"); } }
测试无法加载应用程序上下文,
2018-02-08T10:58:42,385 INFO Neither @ContextConfiguration nor @ContextHierarchy found for test class [io.github.thenilesh.service.impl.FooServiceTest], using DelegatingSmartContextLoader 2018-02-08T10:58:42,393 INFO Could not detect default resource locations for test class [io.github.thenilesh.service.impl.FooServiceTest]: no resource found for suffixes {-context.xml}. 2018-02-08T10:58:42,394 INFO Could not detect default configuration classes for test class [io.github.thenilesh.service.impl.FooServiceTest]: FooServiceTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration. 2018-02-08T10:58:42,432 INFO Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, (...)org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener] 2018-02-08T10:58:42,448 INFO Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@f0ea28, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@16efaab,(...)org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@9604d9] 2018-02-08T10:58:42,521 INFO Refreshing org.springframework.context.support.GenericApplicationContext@173f9fc: startup date [Thu Feb 08 10:58:42 IST 2018]; root of context hierarchy 2018-02-08T10:58:42,606 INFO JSR-330 'javax.inject.Inject' annotation found and supported for autowiring 2018-02-08T10:58:42,666 ERROR Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@19aaa5] to prepare test instance [io.github.thenilesh.service.impl.FooServiceTest@57f43] org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'io.github.thenilesh.service.impl.FooServiceTest': Unsatisfied dependency expressed through field 'fooService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'io.github.thenilesh.service.FooService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE] . . . at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) [.cp/:?] Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'io.github.thenilesh.service.FooService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1493) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE] ... 28 more 2018-02-08T10:58:42,698 INFO Closing org.springframework.context.support.GenericApplicationContext@173f9fc: startup date [Thu Feb 08 10:58:42 IST 2018]; root of context hierarchy
完整堆栈跟踪
如果使用@SpringBootTest注释测试类,那么它会创建整个应用程序上下文,包括数据库连接和许多不相关的bean,这显然不需要进行unit testing(那时它不会进行单元测试!)。 可以预期的是,除了使用@MockBean
之外,只应该实例化FooService
依赖的@MockBean
。
您应该使用@SpringBootTest(classes=FooServiceImpl.class)
。
正如它在注释类型SpringBootTest中提到的:
public abstract Class []类
用于加载ApplicationContext的带注释的类。 也可以使用@ContextConfiguration(classes = …)指定。 如果没有定义显式类,则测试将在返回SpringBootConfiguration搜索之前查找嵌套的@Configuration类。
返回:用于加载应用程序上下文的带注释的类另请参见:ContextConfiguration.classes()
默认值:{}
这只会加载必要的类。 如果不指定,它可能会加载数据库配置和其他会使测试速度变慢的东西。
另一方面,如果你真的想要unit testing,你可以在没有Spring的情况下测试这段代码 – 然后@RunWith(SpringRunner.class)
和@SpringBootTest
注释是没有必要的。 您可以测试FooServiceImpl
实例。 如果您有自动Autowired
/注入的属性或服务,则可以通过setter,构造函数或使用Mockito进行模拟来设置它们。
unit testing应该单独测试组件。 您甚至不需要使用Spring Test上下文框架进行unit testing。 您可以使用模拟框架(如Mockito,JMock或EasyMock)来隔离组件中的依赖项并validation期望。
如果您想要真正的集成测试,那么您需要在测试类上使用@SpringBootTest注释。 如果您不指定classes属性,则会加载@SpringBootApplication带注释的类。 这导致生成组件,如数据库连接被加载。
为了消除这些,需要定义一个单独的测试配置类,例如定义嵌入式数据库而不是生产数据库
@SpringBootTest(classes = TestConfiguration.class) public class ServiceFooTest{ } @Configuration @Import(SomeProductionConfiguration.class) public class TestConfiguration{ //test specific components }
- log4j2 – Syslog appender和PatternLayout
- 如何将列表作为来自jersey2客户端的响应
- 嘲弄保护方法
- AspectJ + Junit + Maven – 在测试中识别切入点但NoSuchMethodError:aspectOf()exception抛出
- 如何在Java(整数)中生成唯一ID?
- 如何在不重写serialize方法的情况下覆盖特定类的JsonSerializer的isEmpty方法?
- 使用Apache POI API将xlsx转换为csv
- 如何解决找不到媒体类型= multipart / form-data错误的MessageBodyWriter
- 正则表达式替换所有忽略大小写