全部产品

单元测试

SOFABoot 工程原型提供两类单元测试:

本文将以 SOFABoot Web 工程的测试类为例,对测试类的测试逻辑进行说明。

说明

SOFABoot Core 工程的测试类,测试逻辑比较简单,可参考 SOFABoot Web 工程,不再详细说明。

测试类路径:

  • 测试基类(AbstractTestBasesrc):

    projectName/app/web/src/main/test/java/.../base/AbstractTestBase.java

  • 测试子类(SofaRestServiceTest): projectName/app/web/src/main/test/java/.../usercases/SofaRestServiceTest.java

更多单元测试的资料可以参考 Spring Boot 官方文档

测试基类

示例代码

以下为默认生成的 AbstractTestBase.java 测试基类的示例代码:

/**
 * 参考文档: http://docs.spring.io/spring-boot/docs/1.4.2.RELEASE/reference/htmlsingle/#boot-features-testing
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SOFABootWebSpringApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public abstract class AbstractTestBase {

    public static final String SOFA_REST_PORT = "8341";

    @Autowired
    public EmbeddedWebApplicationContext server;

    /**
     * 8080
     */
    @LocalServerPort
    public int definedPort;


    @Autowired
    public TestRestTemplate testRestTemplate;

    public String urlHttpPrefix;

    public String sofaRestHttpPrefix;

    @Before
    public void setUp() {
        sofaRestHttpPrefix = "http://localhost:" + SOFA_REST_PORT;
        urlHttpPrefix = "http://localhost:" + definedPort;
        childSetUp();
    }

    /**
     * 测试子类每个方法执行前需要进行的初始化代码放在此
     */
    public abstract void childSetUp();

}

代码解读

  • @SpringBootTest 的参数:

    • SOFABootWebSpringApplication 类:是 SOFABoot 的启动函数。在运行单元测试时,能直接启动 SOFABoot 框架,并完成所有依赖中间件的自动配置和启动。

    • webEnvironment:用以配置 Servlet 环境。DEFINED_PORT 表示,该模式会提供一个真实的 Servlet 环境,使用内嵌的容器,但是使用定义好的端口。

  • 注入的 bean:

    • EmbeddedWebApplicationContext 实例:用于测试用户上下文。

    • TestRestTemplate 实例:用于测试 REST 功能的模板实例。

测试子类

示例代码

以下为默认生成的 SofaRestServiceTest.java 测试类的示例代码:

public class SofaRestServiceTest extends AbstractTestBase {

    @Test
    public void testSofaRestGet() throws Exception {
        assertNotNull(testRestTemplate);
        String restUrl = sofaRestHttpPrefix + "/webapi/users/xiaoming";
//注意:RestSampleFacadeResp 一定要有默认构造函数
        ResponseEntity<RestSampleFacadeResp> response = testRestTemplate.getForEntity(restUrl, RestSampleFacadeResp.class);
        RestSampleFacadeResp restSampleFacadeResp = response.getBody();
        assertTrue(restSampleFacadeResp.isSuccess());
//注意:这里仅供测试使用,使用的是 jackson 不支持泛型的反序列化,所以被反序列化为 map。
//  使用详情请参考文档:http://docs.spring.io/spring-boot/docs/1.4.2.RELEASE/reference/htmlsingle/#boot-features-testing
        Map<String, Integer> demoUserModel = (Map<String, Integer>) restSampleFacadeResp.getData();
        assertTrue(demoUserModel.get("userId") >= 0);
    }
}

代码解读

  • 通过继承关系,子类可以从基类中获取下述信息:

    • 基类注入的 TestRestTemplate 实例

    • REST 端口号 8341

    • 待测试的 SOFAREST 的 URL:用来测试 SOFAREST 的功能,根据返回的数据结果进行校验

  • 测试逻辑

    • TestRestTemplate 会发起 restUrl 请求,并返回 response 对象。

    • response 对象通过 getBody() 方法,获取 restSampleFacadeResp 对象,通过 assertTrue() 方法对获取的数据进行判断。

    • restSampleFacadeResp 对象通过 getData() 方法,获取 SampleRestFacadeRestImpl 中的默认数据,并通过 assertTrue() 方法对获取的数据进行判断。

  • 测试结果验证

    • 方式 1:可以通过 IDEA 运行测试类 SofaRestServiceTest,通过测试时,控制台会提示:“Tests passed: 1 of 1 test”。

    • 方式 2:运行 SOFABootWebSpringApplication 类,然后在浏览器地址栏输入:http://localhost:8341/webapi/users/xiaoming,则输出结果如下图所示。

      单元测试

常见问题

项目正常启动,REST 无法正常访问,报错信息提示 processValue() 方法异常。

现象

  • 运行 SOFABootWebSpringApplication 类,项目正常启动。

  • 在浏览器地址栏输入:http://localhost:8341/webapi/users/xiaoming 后,没有输出结果。

  • 查看 /logs/rest/common-error.log ,有下述报错信息:

    [SOFA-REST-WORK-3-1] ERROR c.a.sofa.rest.server.netty.support.RequestHandler-Unexpected java.lang.NoSuchMethodError: com.alibaba.fastjson.serializer.JavaBeanSerializer.processValue(Lcom/alibaba/fastjson/serializer/JSONSerializer;Lcom/alibaba/fastjson/serializer/BeanContext;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;Ljava/lang/Integer

原因

上述错误主要是由于 Fastjson 版本 1.2.62 不兼容 1.2.60 以前的方法 JavaBeanSerializer.processValue 造成的。

解决方案

可以在主 pom.xml 中引入 fastjson 1.2.60 版本的依赖即可解决,示例如下:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.60</version>
</dependency>