【Spring Cloud 基础设施搭建系列】Spring Cloud Demo项目 Spring Boot Test集成测试环境搭建

集成测试

集成测试(Integration Testing,缩写为INT)将多个模块结合在一起进行测试,确保多个组件可以正确交互。当它失败表示你的各个代码块间无法有效协作。

集成测试可以是下面其中一项:

  • 测试两个或多个类之间的交互。
  • 包含多层的测试。包括业务服务和持久层之间的交互。
  • 包含整个应用程序整个路径的测试。在这些测试中,我们向应用程序发送一个请求,并检查它是否正确响应并根据我们的期望更改了数据库状态。

@SpringBootTest的集成测试

Spring Boot提供了@SpringBootTest注释,可用于创建一个的application context上下文,这个application context中的对象(比如server层,dao层的对象)都是被Spring真实管理的,并不是通过mock方式虚拟出来的。但是请注意,过度使用@SpringBootTest 可能会导致测试的运行时间很长。

事实上,我们应该用更多的单元测试去覆盖我们的代码,因为在自动化测试的金字塔中,单元测试的占比应该是最大的,在单元测试中,我们可以手动创建需要测试的对象,然后通过Stub和Mock来模拟这些外部依赖的对象。比如测试Controller层的时候,我们可以把service层Mock掉,测试service层的时候我们可以把dao层Mock掉。尽可能多的单元测试来代替集成测试,因为使用集成测试需要Spring在每次启动测试时都启动整个应用程序上下文,会导致自动化测试的运行时间过长。而且一旦应用程序变大,Spring需要花更多的时间,因为Spring必须将越来越多的bean加载到应用程序上下文中。

我个人认为应该把每个测试的方法拆分的更加细,不是简单从请求到DB的流程,而是应该专注你需要测试的业务逻辑,可以把依赖的接口或者不是你关注重点的依赖数据都可以Mock和Stub掉。用更多的单元测试去覆盖,这样一方面可以减少自动化测试的运行时间,因为不需要启动整个应用程序上下文,而且更加能保证代码的质量。

当然,对于需要覆盖从传入请求到数据库的整个Spring Boot应用程序的测试,或者需要测试到与数据库交互或者业务服务和持久层之间交互的,我们可以并且应该使用@SpringBootTest。

Spring Boot Test集成测试环境搭建

  1. 首先我这里提取了一个专门测试用的module,用来放跟测试有关的common类,还有管理测试相关的依赖包

  2. 我们加入测试需要的依赖。

       <dependency>
            <groupId>com.jayway.jsonpath</groupId>
            <artifactId>json-path</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.javafaker</groupId>
            <artifactId>javafaker</artifactId>
        </dependency>
        <dependency>
            <groupId>com.cc.cloud</groupId>
            <artifactId>cloud-common</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>provided</scope>
        </dependency>

并在parent pom文件中管理依赖的版本。

            <dependency>
                <groupId>com.jayway.jsonpath</groupId>
                <artifactId>json-path</artifactId>
                <version>2.4.0</version>
            </dependency>
            <dependency>
                <groupId>com.github.javafaker</groupId>
                <artifactId>javafaker</artifactId>
                <version>1.0.0</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.9.7</version>
            </dependency>

下面介绍一下上面的一些依赖的作用。

  • json-path 是json解析工具,当我们测试controller层的时候,可以很方便的解析并测试我们返回的json数据。
  • javafaker是测试造数据的神器,可以帮我们生成姓名,地址之类的测试数据。
  • cloud-common 这个是我抽取的一些common需要用到的类或者依赖。
  • spring-tx和jackson-databind和spring-boot-starter-web是我们一会抽取测试基类的时候会用到的依赖。
  1. 抽取测试基类
package com.cc.cloud.test.common;

import com.github.javafaker.Faker;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Ignore;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;


@RunWith(SpringRunner.class)
@ActiveProfiles({"test"})
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)//目录层次结构与主类一致,classes属性可以不指定启动类
@FixMethodOrder(MethodSorters.NAME_ASCENDING) //JUnit中按照字母顺序执行测试
@Transactional //@Transactional 注释标签是表明此测试类的事务启用,这样所有的测试方案都会自动的rollback,即不用自己清除自己所做的任何对数据库的变更了
@Ignore
public abstract class IntegrationTestBase {

    private Faker faker;

    public Faker getFaker() {
        return this.faker;
    }

    @Before
    public void initIntegrationTestBase() {
        this.faker = new Faker();
    }
}


这里抽取了一个测试的基类IntegrationTestBase,用于所有的集成测试的基类。

然后下面有抽取了一个测试的基类,继承了IntegrationTestBase。用于所有Controller层测试的基类。

package com.cc.cloud.test.common;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Before;
import org.junit.Ignore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@Ignore
public abstract class ControllerTestBase extends IntegrationTestBase {
    @Autowired
    private WebApplicationContext webApplicationContext;
    private MockMvc mockMvc;
    private ObjectMapper jsonMapper;

    @Before
    public void initControllerTestBase() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
        this.jsonMapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }

    public MockMvc getMockMvc() {
        return this.mockMvc;
    }

    public ObjectMapper getJsonMapper() {
        return this.jsonMapper;
    }
}


  1. Spring Boot Test集成测试的配置。

我们创建一个bootstrap-test.yml文件,首先为什么是这个命名呢,因为我们前面指定了@ActiveProfiles({"test"}),所以我们profiles指定了test,所以后缀需要加上test。但是为什么不是application-test.yml文件呢?因为我试过在application-test.yml配置,但是发现测试运行是报错的。

21:24:06.698 [main] DEBUG org.springframework.test.context.junit4.SpringJUnit4ClassRunner - SpringJUnit4ClassRunner constructor called with [class com.cc.cloud.member.controller.MemberControllerTest]
21:24:06.719 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate]
21:24:06.760 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)]
21:24:06.858 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [com.cc.cloud.member.controller.MemberControllerTest] from class [org.springframework.boot.test.context.SpringBootTestContextBootstrapper]
21:24:06.907 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [com.cc.cloud.member.controller.MemberControllerTest], using SpringBootContextLoader
21:24:06.916 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.cc.cloud.member.controller.MemberControllerTest]: class path resource [com/cc/cloud/member/controller/MemberControllerTest-context.xml] does not exist
21:24:06.919 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.cc.cloud.member.controller.MemberControllerTest]: class path resource [com/cc/cloud/member/controller/MemberControllerTestContext.groovy] does not exist
21:24:06.919 [main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [com.cc.cloud.member.controller.MemberControllerTest]: no resource found for suffixes {-context.xml, Context.groovy}.
21:24:06.922 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [com.cc.cloud.member.controller.MemberControllerTest]: MemberControllerTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
21:24:07.471 [main] DEBUG org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider - Identified candidate component class: file [C:\Users\c\Desktop\spring-cloud-demo\cloud-service-member\target\classes\com\cc\cloud\member\MemberApp.class]
21:24:07.473 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration com.cc.cloud.member.MemberApp for test class com.cc.cloud.member.controller.MemberControllerTest
21:24:08.010 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - @TestExecutionListeners is not present for class [com.cc.cloud.member.controller.MemberControllerTest]: using defaults.
21:24:08.011 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - 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.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
21:24:08.116 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@57d7f8ca, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@76c3e77a, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@78123e82, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@67c33749, org.springframework.test.context.support.DirtiesContextTestExecutionListener@fba92d3, org.springframework.test.context.transaction.TransactionalTestExecutionListener@662b4c69, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@fa49800, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@71238fc2, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@2a54a73f, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@16a0ee18, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@3d6f0054, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@505fc5a4]
21:24:08.121 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.cc.cloud.member.controller.MemberControllerTest]
21:24:08.122 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.cc.cloud.member.controller.MemberControllerTest]21:24:08.142 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.cc.cloud.member.controller.MemberControllerTest]
21:24:08.142 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.cc.cloud.member.controller.MemberControllerTest]21:24:08.151 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.cc.cloud.member.controller.MemberControllerTest]
21:24:08.151 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.cc.cloud.member.controller.MemberControllerTest]
21:24:08.153 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.cc.cloud.member.controller.MemberControllerTest]
21:24:08.153 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.cc.cloud.member.controller.MemberControllerTest]
21:24:08.214 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - Before test class: context [DefaultTestContext@21e360a testClass = MemberControllerTest, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@5ba3f27a testClass = MemberControllerTest, locations = '{}', classes = '{class com.cc.cloud.member.MemberApp}', contextInitializerClasses = '[]', activeProfiles = '{test}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true, server.port=0}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@226a82c4, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@6b53e23f, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@15bb6bea, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@45f45fa1], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> false]], class annotated with @DirtiesContext [false] with mode [null].
21:24:08.215 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.cc.cloud.member.controller.MemberControllerTest]
21:24:08.215 [main] DEBUG org.springframework.test.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.cc.cloud.member.controller.MemberControllerTest]
21:24:08.276 [main] DEBUG org.springframework.test.context.support.DependencyInjectionTestExecutionListener - Performing dependency injection for test context [[DefaultTestContext@21e360a testClass = MemberControllerTest, testInstance = com.cc.cloud.member.controller.MemberControllerTest@434a63ab, testMethod = [null], testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@5ba3f27a testClass = MemberControllerTest, locations = '{}', classes = '{class com.cc.cloud.member.MemberApp}', contextInitializerClasses = '[]', activeProfiles = '{test}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true, server.port=0}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@226a82c4, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@6b53e23f, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@15bb6bea, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@45f45fa1], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> false]]].
21:24:08.335 [main] DEBUG org.springframework.test.context.support.TestPropertySourceUtils - Adding inlined properties to environment: {spring.jmx.enabled=false, org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true, server.port=0}
2019-11-07 21:24:14.632  INFO [cloud-service-member,,,] 1552 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.retry.annotation.RetryConfiguration' of type [org.springframework.retry.annotation.RetryConfiguration$$EnhancerBySpringCGLIB$$e4319f5] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-11-07 21:24:14.666  INFO [cloud-service-member,,,] 1552 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$349984cd] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2019-11-07 21:24:19.171  INFO [cloud-service-member,,,] 1552 --- [           main] o.s.cloud.commons.util.InetUtils         : Cannot determine local hostname
2019-11-07 21:24:19.195  INFO [cloud-service-member,,,] 1552 --- [           main] o.s.c.n.eureka.InstanceInfoFactory       : Setting initial instance status as: STARTING
2019-11-07 21:24:19.297  INFO [cloud-service-member,,,] 1552 --- [           main] com.netflix.discovery.DiscoveryClient    : Initializing Eureka in region us-east-1
2019-11-07 21:24:21.815  INFO [cloud-service-member,,,] 1552 --- [           main] c.n.d.provider.DiscoveryJerseyProvider   : Using JSON encoding codec LegacyJacksonJson
2019-11-07 21:24:21.816  INFO [cloud-service-member,,,] 1552 --- [           main] c.n.d.provider.DiscoveryJerseyProvider   : Using JSON decoding codec LegacyJacksonJson
2019-11-07 21:24:22.035  INFO [cloud-service-member,,,] 1552 --- [           main] c.n.d.provider.DiscoveryJerseyProvider   : Using XML encoding codec XStreamXml
2019-11-07 21:24:22.035  INFO [cloud-service-member,,,] 1552 --- [           main] c.n.d.provider.DiscoveryJerseyProvider   : Using XML decoding codec XStreamXml
2019-11-07 21:24:22.562  INFO [cloud-service-member,,,] 1552 --- [           main] c.n.d.s.r.aws.ConfigClusterResolver      : Resolving eureka endpoints via configuration
2019-11-07 21:24:24.749  INFO [cloud-service-member,,,] 1552 --- [           main] com.netflix.discovery.DiscoveryClient    : Disable delta property : false
2019-11-07 21:24:24.749  INFO [cloud-service-member,,,] 1552 --- [           main] com.netflix.discovery.DiscoveryClient    : Single vip registry refresh property : null
2019-11-07 21:24:24.749  INFO [cloud-service-member,,,] 1552 --- [           main] com.netflix.discovery.DiscoveryClient    : Force full registry fetch : false
2019-11-07 21:24:24.749  INFO [cloud-service-member,,,] 1552 --- [           main] com.netflix.discovery.DiscoveryClient    : Application is null : false
2019-11-07 21:24:24.750  INFO [cloud-service-member,,,] 1552 --- [           main] com.netflix.discovery.DiscoveryClient    : Registered Applications size is zero : true
2019-11-07 21:24:24.750  INFO [cloud-service-member,,,] 1552 --- [           main] com.netflix.discovery.DiscoveryClient    : Application version is -1: true
2019-11-07 21:24:24.750  INFO [cloud-service-member,,,] 1552 --- [           main] com.netflix.discovery.DiscoveryClient    : Getting all instance registry info from the eureka server
2019-11-07 21:24:28.885 ERROR [cloud-service-member,,,] 1552 --- [           main] c.n.d.s.t.d.RedirectingEurekaHttpClient  : Request execution error. endpoint=DefaultEndpoint{ serviceUrl='http://localhost:8888/eureka/}

com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect
	at com.sun.jersey.client.apache4.ApacheHttpClient4Handler.handle(ApacheHttpClient4Handler.java:187) ~[jersey-apache-client4-1.19.1.jar:1.19.1]
	at com.sun.jersey.api.client.filter.GZIPContentEncodingFilter.handle(GZIPContentEncodingFilter.java:123) ~[jersey-client-1.19.1.jar:1.19.1]
	at com.netflix.discovery.EurekaIdentityHeaderFilter.handle(EurekaIdentityHeaderFilter.java:27) ~[eureka-client-1.9.8.jar:1.9.8]
	at com.sun.jersey.api.client.Client.handle(Client.java:652) ~[jersey-client-1.19.1.jar:1.19.1]
	at com.sun.jersey.api.client.WebResource.handle(WebResource.java:682) ~[jersey-client-1.19.1.jar:1.19.1]
	at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74) ~[jersey-client-1.19.1.jar:1.19.1]
	at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:509) ~[jersey-client-1.19.1.jar:1.19.1]
	at com.netflix.discovery.shared.transport.jersey.AbstractJerseyEurekaHttpClient.getApplicationsInternal(AbstractJerseyEurekaHttpClient.java:194) ~[eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.shared.transport.jersey.AbstractJerseyEurekaHttpClient.getApplications(AbstractJerseyEurekaHttpClient.java:165) ~[eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator$6.execute(EurekaHttpClientDecorator.java:137) [eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.shared.transport.decorator.MetricsCollectingEurekaHttpClient.execute(MetricsCollectingEurekaHttpClient.java:73) ~[eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.getApplications(EurekaHttpClientDecorator.java:134) [eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator$6.execute(EurekaHttpClientDecorator.java:137) [eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.shared.transport.decorator.RedirectingEurekaHttpClient.executeOnNewServer(RedirectingEurekaHttpClient.java:118) ~[eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.shared.transport.decorator.RedirectingEurekaHttpClient.execute(RedirectingEurekaHttpClient.java:79) ~[eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.getApplications(EurekaHttpClientDecorator.java:134) [eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator$6.execute(EurekaHttpClientDecorator.java:137) [eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.shared.transport.decorator.RetryableEurekaHttpClient.execute(RetryableEurekaHttpClient.java:120) [eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.getApplications(EurekaHttpClientDecorator.java:134) [eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator$6.execute(EurekaHttpClientDecorator.java:137) [eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.shared.transport.decorator.SessionedEurekaHttpClient.execute(SessionedEurekaHttpClient.java:77) [eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.getApplications(EurekaHttpClientDecorator.java:134) [eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.DiscoveryClient.getAndStoreFullRegistry(DiscoveryClient.java:1051) [eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.DiscoveryClient.fetchRegistry(DiscoveryClient.java:965) [eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.DiscoveryClient.<init>(DiscoveryClient.java:414) [eureka-client-1.9.8.jar:1.9.8]
	at com.netflix.discovery.DiscoveryClient.<init>(DiscoveryClient.java:269) [eureka-client-1.9.8.jar:1.9.8]
	at org.springframework.cloud.netflix.eureka.CloudEurekaClient.<init>(CloudEurekaClient.java:63) [spring-cloud-netflix-eureka-client-2.1.0.RC2.jar:2.1.0.RC2]
	at org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration$EurekaClientConfiguration.eurekaClient(EurekaClientAutoConfiguration.java:249) [spring-cloud-netflix-eureka-client-2.1.0.RC2.jar:2.1.0.RC2]
	at org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration$EurekaClientConfiguration$$EnhancerBySpringCGLIB$$983fd3cd.CGLIB$eurekaClient$2(<generated>) [spring-cloud-netflix-eureka-client-2.1.0.RC2.jar:2.1.0.RC2]
	at org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration$EurekaClientConfiguration$$EnhancerBySpringCGLIB$$983fd3cd$$FastClassBySpringCGLIB$$593f1583.invoke(<generated>) [spring-cloud-netflix-eureka-client-2.1.0.RC2.jar:2.1.0.RC2]
	at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) [spring-core-5.1.3.RELEASE.jar:5.1.3.RELEASE]

这是为什么呢?因为我们测试的这个微服务中配置了spring config client,并且我们已经把我们的配置抽取到了远程的配置中心了。所以当启动测试服务的时候,会先读取bootstrap.yml,然后连接eureka服务,找到config server,然后config client请求config server去读取远程的配置。 但是由于我们的eureka和config server在测试环境下都是没有启动的,自然就会出现连接错误的问题。

这种时候我们需要怎么解决呢?我的解决方法如下:

因为当我们设置了profiles为test的时候,其实会先读取bootstrap.yml然后再读取bootstrap-test.yml,而且后面的配置会覆盖前面的配置。最后结合这两个配置去启动服务。所以我们可以在bootstrap-test.yml去设置关闭我们的eureka还有config client。

下面给我的配置:

spring:
  datasource:
    druid:
      # jdbc:h2:mem:指定databaseName; 内存模式
      # DB_CLOSE_DELAY=-1 关闭连接后数据库将被清空,适合测试环境
      # MODE=MYSQL 兼容模式为MYSQL
      # DB_CLOSE_ON_EXIT=FALSE	 VM存在时不关闭数据库
      url: jdbc:h2:mem:member_service_db;MODE=MYSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
      username: sa
      password: sa
      driver-class-name: org.h2.Driver
    platform: h2
    # schema: classpath:schema.sql //程序运行时,使用schema.sql来创建数据库中的表
    data: classpath:/db/init-data.sql # 程序运行时,使用data.sql来创建初始数据

  jpa:
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    database: h2
    show-sql: true
    hibernate:
      ddl-auto: create-drop # 每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
    properties:
      hibernate:
        show_sql: true # 操作数据库时显示sql语句
        use_sql_comments: true # SQL 语句中输出便于调试的注释信息
        format_sql: true
        dialect: org.hibernate.dialect.MySQL5InnoDBDialect
        jdbc:
          time_zone: UTC
  h2: # 开启h2的控制台
    console:
      enabled: true
      path: /console
      settings:
        trace: false
        web-allow-others: true
  cloud:
    bus:
      enabled: false # 关闭Spring Cloud Bus,关闭连接RabbitMQ
    config:
      enabled: false # disable config
      discovery:
        enabled: false # disable config
    discovery: # disable eureka
      enabled: false
eureka:
  client:
    enabled: false # disable eureka
  1. Controller测试
package com.cc.cloud.member.controller;

import com.cc.cloud.member.domain.Member;
import com.cc.cloud.test.common.ControllerTestBase;
import org.junit.Test;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

public class MemberControllerTest extends ControllerTestBase {

    @Test
    public void findAllMembers() throws Exception {
        this.getMockMvc()
                .perform(
                        get("/member")
                                .contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
                .andDo(MockMvcResultHandlers.print())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(jsonPath("$.code").value(1))
                .andExpect(jsonPath("$.message").value("成功"));
    }

    @Test
    public void addMember() throws Exception {
        String name = getFaker().name().fullName();
        Member member = new Member();
        member.setMemberName(name);
        this.getMockMvc()
                .perform(
                        post("/member")
                                .content(getJsonMapper().writeValueAsString(member))
                                .contentType(MediaType.APPLICATION_JSON)).andExpect(status().isCreated())
                .andDo(MockMvcResultHandlers.print())//输出整个响应结果信息
                .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(jsonPath("$.code").value(1))
                .andExpect(jsonPath("$.message").value("成功"))
                .andExpect(jsonPath("$.result.memberName").value(name));
    }
}
  1. service层测试
package com.cc.cloud.member.service.impl;

import com.cc.cloud.member.domain.Member;
import com.cc.cloud.member.service.MemberService;
import com.cc.cloud.test.common.IntegrationTestBase;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

public class MemberServiceImplTest extends IntegrationTestBase {

    @Autowired
    private MemberService memberService;


    @Test
    public void testAddMember() {
        String name = getFaker().name().fullName();
        Member member = new Member();
        member.setMemberName(name);
        Member addMember = memberService.addMember(member);
        Assert.assertEquals(name, addMember.getMemberName());
    }

    @Test
    public void testFindAllMembers() {
        Assert.assertEquals(1, memberService.findAllMembers().size());
    }
}

具体的配置和代码我这里就不多解释了,有些也加了注释,有兴趣的可以参考下面的参考资料。

参考

自动化测试最佳实践(一):从纺锤模型到金字塔模型

Integration Tests with @SpringBootTest

Testing Spring MVC Web Controllers with @WebMvcTest

Spring中文文档翻译Integration Testing(1)

springboot Junit单元测试之坑–@SpringBootTest注解无法加载src/main/resources目录下资源文件

Spring整合Junit4进行单元测试

SpringBoot 优雅的进行单元测试

SpringBoot2.X (十四): @SpringBootTest单元测试

SpringBoot整合Spring Data JPA、MySQL、Druid并使用Mockito实现单元测试

使用H2Database+Druid连接池+Spring Data JPA+Ehcache实现CRUD操作

SpringBoot系列(十)单元测试

Spring MVC – 单元测试和集成测试

Springboot 中使用单元测试

内存数据库-H2简介与实践

H2数据库攻略

H2 Features

How to disable Eureka and Spring Cloud Config in a WebMvcTest?

How to selectively disable Eureka discovery client with Spring?

使用 Spring 进行单元测试

JUnit中按照顺序执行测试方式

使用MockMvc进行Junit单元测试

SpringMVC单元测试-MockMvc

MockMVC - 基于RESTful风格的SpringMVC的测试

Spring Boot 的单元测试和集成测试

Lombok与javaFaker的使用,解放劳动力

java测试造数据神器JavaFaker

使用com.jayway.jsonpath.JsonPath包进行JSON的快速解析、设置值需要注意的性能提升方法

一款好用的json解析工具,JsonPath

源代码

https://gitee.com/cckevincyh/spring-cloud-demo/tree/int-test/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值