Springboot08-项目单元测试(接口测试)

Springboot08-项目单元测试(接口测试)

前言

  1-本文重点在于源码层面,分析Springboot单元测试的使用,对于其中的注解、方法等,不会仔细分析;

  2-本文项目实例相关配置:Java-1.8;  Springboot-1.5.12.RELEASE,使用的是Intellij

 

正文

1- JUnitGenerator V2.0 单元测试代码自动生成插件的使用

因为插件的安装和使用,比较简单,见参考资料-1,或自行百度;这里不再赘述;

注意:JUnitGenerator主要是可以针对需要测试的类,在test目录下快速生成对应的测试类,而测试的具体实现还是要手动处理;

2-最简单的单元测试,源码如下:

package com.hs.web.controller.system.login;
import net.sf.json.JSONObject;

@RunWith(SpringRunner.class)
@SpringBootTest(classes= FrameworkApplication.class)
public class LoginControllerTest {

    private MockMvc mvc;
    
    @Autowired
    private WebApplicationContext context;

    @Before
    public void setUp() throws Exception {
        mvc = MockMvcBuilders.webAppContextSetup(context).build();
    }
    @Test
    public void login() throws Exception{
        //1-封装请求参数
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("openid", "tangyujie");
        JSONObject jsonObject = JSONObject.fromObject(paramMap);
        //2-构建请求
        RequestBuilder builder = MockMvcRequestBuilders.post("/api/shop/login").content(jsonObject.toString()).
                accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON);
        //3-执行请求
        mvc.perform(builder)
            .andExpect(status().isOk())
            .andExpect(jsonPath("code").value(200))
            .andDo(MockMvcResultHandlers.print());
    }
    @After
    public void after(){

    }
}

 如果只测试一个接口,或者只测试一个类,那么上面的方法基本上已经可以满足需求,但是针对上百个类或方法的情况,则会出现大量的代码冗余,并且还需要手动启动每一个测试类,这明显是不可取的;

所以,在上述简单的单元测试方法上,进行封装

3-封装通用的单元测试模板

单元测试模板可以满足如下需求:

  • 抽取公用的方法和注解,优化代码,实现代码复用,也方便维护
  • 多个单元测试类,一个启动入口,实现一键测试

接口测试的步骤

  1. 请求数据
  2. 构造请求
  3. 执行请求及判断请求结果
  4. 处理返回参数(本步骤可以没有)

备注:本项目的接口因为供App使用,(除了登录接口)其他都是要传token(跟在UTL?后面)

源码如下

测试模板目录

 

 BaseTest抽象类,用于封装通用方法

package com.hs.api.base;

import net.sf.json.JSONObject;

public abstract  class BaseTest {

    //1-1-构建post请求
    protected RequestBuilder getPostBuilder(String url, JSONObject jsonObject){
        RequestBuilder builder = MockMvcRequestBuilders
                .post(url)
                .content(jsonObject.toString())
                .accept(MediaType.APPLICATION_JSON)
                .contentType(MediaType.APPLICATION_JSON);
        return builder;
    }
    //1-2-构建get请求
    protected RequestBuilder getGetBuilder(String url){
        RequestBuilder builder = MockMvcRequestBuilders
                .get(url)
                .accept(MediaType.APPLICATION_JSON)
                .contentType(MediaType.APPLICATION_JSON);
        return builder;
    }
    //2-执行请求
    protected MvcResult performMVC(MockMvc mvc, RequestBuilder bulider ){
        try {
            MvcResult mvcResult = mvc.perform(bulider)
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("code").value(200))
                    //.andDo(MockMvcResultHandlers.print())
                    .andReturn();
            return mvcResult;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

BaseControllerTest类,继承 BaseTest抽象类,且是所有单元测试类的父类, 使用了@RunWith、@SpringBootTest等类注解,并且实现了通用的@Before和@After方法,以及登录接口的具体实现,主要是从普通的单元测试类中提取出公有的因素

package com.hs.api.base;
import net.sf.json.JSONObject;
//所有单元测试类的基类
@RunWith(SpringRunner.class)
@SpringBootTest(classes= FrameworkApplication.class)
@WebAppConfiguration
public abstract  class BaseControllerTest extends BaseTest {

    @Autowired
    protected WebApplicationContext context;

    protected MockMvc mvc ;
    protected  static String shopToken = null;//apptoken

    @Before
    protected void setUp() throws Exception {
        System.out.println("controller test is starting");
        mvc = MockMvcBuilders.webAppContextSetup(context).build();
        if(shopToken == null){
            System.out.println("获取shoptoken");
            shopToken = shopLogin();
        }
    }
    @After
    protected void tearDown(){
        System.out.println("controller test is over");
    }


    //1-App登录操作
    protected String shopLogin() {
        //1-请求数据
        String url = "/api/shop/login";
        LoginReq loginReq = new LoginReq("tangyujie");
        JSONObject content = JSONObject.fromObject(loginReq);
        //2-构造请求
        RequestBuilder postBuilder = getPostBuilder(url, content);
        try {
            //3-执行请求-及判断请求结果
            MvcResult mvcResult = performMVC(mvc, postBuilder);
            //4-c处理返回参数,获取shopToken
            String contentAsString = mvcResult.getResponse().getContentAsString();
            //System.out.println("contentAsString=== " + contentAsString);
            JSONObject jsonObject = JSONObject.fromObject(contentAsString);
            Map<String,Class<?>> classMap = new HashMap<String,Class<?>>();
            classMap.put("data", LoginRes.class);
            TestShopLoginRes entity = (TestShopLoginRes)JSONObject.toBean(jsonObject,TestShopLoginRes.class,classMap);
            //System.out.println("entity: " + entity);
            return entity .getData().getShopToken();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}

ApiShopLoginControllerTest类,登录的接口的测试类,继承BaseControllerTest类;本类包含一个POST接口实例

package com.hs.api.shopapp.controller;


public class ApiShopLoginControllerTest  extends BaseControllerTest{
    //POST请求示例
    //1-登录
    @Test
    public void loginOrRegister() {
        shopLogin();
    }
}

 

 ApiShopMemberControllerTest类,用户接口的测试类,继承BaseControllerTest类;本类包含一个GET接口实例

package com.hs.api.shopapp.controller;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

public class ApiShopMemberControllerTest extends BaseControllerTest{

    //GET请求示例
    @Test
    public void getMemberShopInfo() {
        //1-请求参数
        System.out.println("shopToken == " + shopToken);
        String url = "/api/shop/member/detail?shopToken=" + shopToken;
        //2-构建请求
        RequestBuilder getBuilder = getGetBuilder(url);
        try {
            //3-执行请求
            MvcResult mvcResult = performMVC(mvc, getBuilder);
            System.out.println("getMemberShopInfo mvcResult: " + mvcResult);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

 

TestSuits类,本类没有具体的方法,唯一的作用是作为启动入口,测试全部测试类的方法

package com.hs.api;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;

//单元测试总入口,在@Suite.SuiteClasses里面添加需要测试的测试类
@RunWith(Suite.class)
@Suite.SuiteClasses({
        ApiShopLoginControllerTest.class,
        ApiShopMemberControllerTest.class
})
public class TestSuits {
}

以下是模板用到的实体类POJO,主要是用户封装请求参数或返回参数,注意实体类和JSON字符串之间的转换

//请求参数实体类
public class LoginReq {
    private String openid;//openid

    public LoginReq() {
    }

//省略geter和setter方法
}

//返回参数实体父类
package com.hs.api.common;

public class TestResponseEntity {

    public int code;
    public String message;

    public TestResponseEntity() {
    }

//省略geter和setter方法
}
//登录接口返回参数实体类(两个,相互嵌套关系)

public class TestShopLoginRes extends TestResponseEntity {
    private LoginRes data;

    public TestShopLoginRes() {
    }

    public TestShopLoginRes(LoginRes data) {
        this.data = data;
    }

//省略geter和setter方法

    @Override
    public String toString() {
        return "TestShopLoginRes{" +
                "data=" + data +
                '}';
    }
}



public class LoginRes {

    private Long shopId;//
    private int mobileExistFlag;//手机号是否存在
    private String shopToken;//
    private int vipFlag;//是否是会员

    public LoginRes() {
    }


 //省略geter和setter方法

    @Override
    public String toString() {
        return "LoginRes{" +
                "shopId=" + shopId +
                ", mobileExistFlag=" + mobileExistFlag +
                ", shopToken='" + shopToken + '\'' +
                ", vipFlag=" + vipFlag +
                '}';
    }
}

 

以下是实例中测试的实际接口类

//登录接口类
@RestController
@RequestMapping(value="/api/shop/login")
public class ApiShopLoginController extends ApiShopBaseController {

    //1-登陆或注册
    @RequestMapping(value="",method = RequestMethod.POST,produces = {JSON_UTF8})
    public String loginOrRegister(@RequestBody LoginReq loginReqt){
      //省略业务
        }
    }

//用户接口类

@RestController
@RequestMapping(value="/api/shop/member")
public class ApiShopMemberController extends ApiShopBaseController {

    //1-店主基本信息
    @RequestMapping(value = { "/detail" }, method={RequestMethod.GET},produces = {JSON_UTF8})
    public String getMemberShopInfo() throws ServerSqlErrorException {
        //省略业务
    }
}
    

 

 

参考文献:

1-JUnitGenerator使用方法:http://www.pianshen.com/article/516640707/

2-测试用例整合:https://blog.csdn.net/weixin_39800144/article/details/79241620

3-返回参数处理:https://blog.csdn.net/qq_16513911/article/details/83018027

 

转载于:https://www.cnblogs.com/wobuchifanqie/p/10445461.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值