Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么组件扫描不能用于Spring引导单元测试?_Java_Spring_Unit Testing_Spring Boot_Integration Testing - Fatal编程技术网

Java 为什么组件扫描不能用于Spring引导单元测试?

Java 为什么组件扫描不能用于Spring引导单元测试?,java,spring,unit-testing,spring-boot,integration-testing,Java,Spring,Unit Testing,Spring Boot,Integration Testing,服务类FooServiceImpl用aka@Component注释,使其符合自动布线的条件。为什么在单元测试期间没有拾取和自动连接这个类 @Service public class FooServiceImpl implements FooService { @Override public String reverse(String bar) { return new StringBuilder(bar).reverse().toString(); }

服务类
FooServiceImpl
用aka
@Component
注释,使其符合自动布线的条件。为什么在单元测试期间没有拾取和自动连接这个类

@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


若用注释对测试类进行了注释,那个么它将创建整个应用程序上下文,包括数据库连接和许多不相关的bean,这些bean显然不是这个单元测试所需要的(那个么它就不是单元测试了!)。我们期望的是,只有
FooService
依赖的bean才应该被实例化,除了那些被模拟的bean之外,使用
@MockBean

单元测试应该单独测试组件。您甚至不需要使用Spring测试上下文框架进行单元测试。您可以使用Mockito、JMock或EasyMock等模拟框架来隔离组件中的依赖项并验证期望值

如果您想要一个真正的集成测试,那么您需要在测试类上使用@SpringBootTest注释。如果不指定classes属性,它将加载@SpringBootApplication注释类。这将导致加载生产组件,如db连接

为了消除这些问题,定义一个单独的测试配置类,例如定义一个嵌入式数据库而不是生产数据库

@SpringBootTest(classes = TestConfiguration.class)
public class ServiceFooTest{
}

@Configuration
@Import(SomeProductionConfiguration.class)
public class TestConfiguration{
   //test specific components
}

您应该使用
@SpringBootTest(classes=FooServiceImpl.class)

正如上面提到的:

公共抽象类[]类

用于加载ApplicationContext的带注释的类。可以 也可以使用@ContextConfiguration(classes=…)指定。如果没有 定义了显式类,测试将查找嵌套类 @配置类,然后返回到 SpringBootConfiguration搜索

返回: 用于加载应用程序上下文的带注释类另请参见: ContextConfiguration.classes()

默认值: {}

这将只加载必需的类。如果不指定,它可能会加载数据库配置和其他东西,这会使您的测试变慢


另一方面,如果你真的想要单元测试,你可以在没有Spring的情况下测试这段代码,那么
@RunWith(SpringRunner.class)
@SpringBootTest
注释是不必要的。您可以测试
FooServiceImpl
实例。如果您有
自动连接的
/注入的属性或服务,您可以通过setter、constructor或mock来设置它们。

我必须解决一个类似的问题,只需稍加修改。我想分享这方面的细节,认为这可能会给那些遇到类似问题的人提供选择

我想编写集成测试,只加载必要的依赖项,而不是所有的应用程序依赖项。所以我选择使用
@DataJpaTest
,而不是
@SpringBootTest
。而且,我还必须包含
@ComponentScan
来解析@Service bean。然而,当ServiceOne开始使用另一个包中的映射器bean时,我必须指定要用
@ComponentScan
加载的特定包。令人惊讶的是,我甚至不得不为第二个不自动连接此映射器的服务执行此操作。我不喜欢这一点,因为它给读者留下了这样的印象,即该服务依赖于该映射程序,而实际上并非如此。因此,我意识到服务的包结构需要进一步微调,以更准确地表示依赖关系

总之,可以使用@DataJpaTest+@ComponentScan和包名的组合来加载特定于层的依赖项,而不是@SpringBootTest。这甚至可以帮助我们对设计进行微调,以更准确地表示您的依赖关系

设计之前 1。com.java.service.ServiceOneImpl

@Service
public class ServiceOneImpl implements ServiceOne {   
  @Autowired
  private RepositoryOne repositoryOne;    
  @Autowired
  private ServiceTwo serviceTwo;      
  @Autowired
  private MapperOne mapperOne;
}
@Service
public class ServiceTwoImpl implements ServiceTwo {   
  @Autowired
  private RepositoryTwo repositoryTwo;    
}
@Service
public class ServiceOneImpl implements ServiceOne {   
  @Autowired
  private RepositoryOne repositoryOne;    
  @Autowired
  private ServiceTwo serviceTwo;      
  @Autowired
  private MapperOne mapperOne;
}
@Service
public class ServiceTwoImpl implements ServiceTwo {   
  @Autowired
  private RepositoryTwo repositoryTwo;    
}
2。com.java.service.ServiceTwoImpl

@Service
public class ServiceOneImpl implements ServiceOne {   
  @Autowired
  private RepositoryOne repositoryOne;    
  @Autowired
  private ServiceTwo serviceTwo;      
  @Autowired
  private MapperOne mapperOne;
}
@Service
public class ServiceTwoImpl implements ServiceTwo {   
  @Autowired
  private RepositoryTwo repositoryTwo;    
}
@Service
public class ServiceOneImpl implements ServiceOne {   
  @Autowired
  private RepositoryOne repositoryOne;    
  @Autowired
  private ServiceTwo serviceTwo;      
  @Autowired
  private MapperOne mapperOne;
}
@Service
public class ServiceTwoImpl implements ServiceTwo {   
  @Autowired
  private RepositoryTwo repositoryTwo;    
}
3。ServiceOneIntegrationTest

@RunWith(SpringRunner.class)
@DataJpaTest
@ComponentScan({"com.java.service","com.java.mapper"})
public class ServiceOneIntegrationTest {
@RunWith(SpringRunner.class)
@DataJpaTest
@ComponentScan({"com.java.service","com.java.mapper"})
public class ServiceOneIntegrationTest {
4。ServiceTwoIntegrationTest.java

@RunWith(SpringRunner.class)
@DataJpaTest
@ComponentScan({"com.java.service","com.java.mapper"})
public class ServiceTwoIntegrationTest {
@RunWith(SpringRunner.class)
@DataJpaTest
@ComponentScan({"com.java.service.two"}) // CHANGE in the packages
public class ServiceTwoIntegrationTest {
在微调包名称之后 1。com.java.service.one.ServiceOneImpl

@Service
public class ServiceOneImpl implements ServiceOne {   
  @Autowired
  private RepositoryOne repositoryOne;    
  @Autowired
  private ServiceTwo serviceTwo;      
  @Autowired
  private MapperOne mapperOne;
}
@Service
public class ServiceTwoImpl implements ServiceTwo {   
  @Autowired
  private RepositoryTwo repositoryTwo;    
}
@Service
public class ServiceOneImpl implements ServiceOne {   
  @Autowired
  private RepositoryOne repositoryOne;    
  @Autowired
  private ServiceTwo serviceTwo;      
  @Autowired
  private MapperOne mapperOne;
}
@Service
public class ServiceTwoImpl implements ServiceTwo {   
  @Autowired
  private RepositoryTwo repositoryTwo;    
}
2。com.java.service.two.ServiceTwoImpl

@Service
public class ServiceOneImpl implements ServiceOne {   
  @Autowired
  private RepositoryOne repositoryOne;    
  @Autowired
  private ServiceTwo serviceTwo;      
  @Autowired
  private MapperOne mapperOne;
}
@Service
public class ServiceTwoImpl implements ServiceTwo {   
  @Autowired
  private RepositoryTwo repositoryTwo;    
}
@Service
public class ServiceOneImpl implements ServiceOne {   
  @Autowired
  private RepositoryOne repositoryOne;    
  @Autowired
  private ServiceTwo serviceTwo;      
  @Autowired
  private MapperOne mapperOne;
}
@Service
public class ServiceTwoImpl implements ServiceTwo {   
  @Autowired
  private RepositoryTwo repositoryTwo;    
}
3。ServiceOneIntegrationTest

@RunWith(SpringRunner.class)
@DataJpaTest
@ComponentScan({"com.java.service","com.java.mapper"})
public class ServiceOneIntegrationTest {
@RunWith(SpringRunner.class)
@DataJpaTest
@ComponentScan({"com.java.service","com.java.mapper"})
public class ServiceOneIntegrationTest {
4。ServiceTwoIntegrationTest.java

@RunWith(SpringRunner.class)
@DataJpaTest
@ComponentScan({"com.java.service","com.java.mapper"})
public class ServiceTwoIntegrationTest {
@RunWith(SpringRunner.class)
@DataJpaTest
@ComponentScan({"com.java.service.two"}) // CHANGE in the packages
public class ServiceTwoIntegrationTest {

您是否尝试过@SpringBootTest(classes=FooServiceImpl.class)?您需要一个上下文这一事实已经使它成为一个集成测试。如果需要单元测试,只需创建存储库的一个实例,模拟依赖项即可。只要您需要一个上下文,它就是一个集成测试。@正如您的注释应该是一个答案,以及“单元测试中Spring引导组件扫描的说明”