文档章节

单元测试——控制层单元测试

李阳-kevin
 李阳-kevin
发布于 2017/09/07 17:55
字数 1207
阅读 32
收藏 0

主要依赖:

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.12</version>

</dependency>

 

<dependency>

<groupId>org.mockito</groupId>

<artifactId>mockito-core</artifactId>

</dependency>

 

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-test</artifactId>

</dependency>

 

<dependency>

<groupId>org.assertj</groupId>

<artifactId>assertj-core</artifactId>

<version>2.5.0</version>

</dependency>

 

在spring-boot项目中只需要如下依赖:

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

</dependency>

 

主要代码:

import com.demo.app.DemoApplication;
import com.demo.common.http.OperationCode;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.BDDMockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

/**
 * Author: liyang
 * Date: 03/09/2017 9:48 AM
 * Version: 1.0
 * Desc: 对普通RESTful服务进行基本的测试。
 */
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = DemoApplication.class)
@DirtiesContext
public class DemoControllerTest {
    @Autowired
    private WebApplicationContext context;

    @MockBean
    private DemoService demoService;

    private MockMvc mockMvc;
    
    // 配置认证信息,以绕过 token 校验 
    /* @Autowired 
     * private OAuthHelper oAuthHelper;
     * private String clientId; 
     * private String[] roles; 
     * private String[] scopes; 
     * private String[] resourceIds;
     */

    @Before
    public void setupMockMvc() {
    // 如果需要认证
    /* clientId = "TMISWeb";
     * roles = new String[]{"read", "write", "admin", "admin_authority"}; 
     * scopes = new String[]{"read", "write", "admin"}; 
     * resourceIds = null;
     */

        mockMvc = MockMvcBuilders
                .webAppContextSetup(context)
              //.apply(springSecurity()) // 使用 spring security 进行认证验证
                .build();
    }

    @Test
    public void findFirst() throws Exception {

        //mock datas
        Demo demo = new Demo();
        demo.setId(1);
        demo.setAge(0);
        //mock service level method invoke
        BDDMockito.when(demoService.findFirst()).thenReturn(demo);

        //assertions
        mockMvc.perform(get("/demo/first")
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON_UTF8)
             // .with(oAuthHelper.bearerToken(clientId, scopes, roles, resourceIds)) //携带认证信息
                )
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
                .andExpect(content().string(containsString("1")))
                .andExpect(content().json("{\"operationCode\":1,\"body\":[{\"id\":1,\"age\":0}"))
                .andExpect(jsonPath("$", not(nullValue())))
                .andExpect(jsonPath("$.operationCode", is(OperationCode.SUCCESS.getValue())))
                .andExpect(jsonPath("$.operationCode").value(1))
                .andExpect(jsonPath("$.body").isNotEmpty());
    }
}

很重要:

1.    对于post、put方法,被测试方法中有@RequestBody修饰的参数,需要添加 .contentType(MediaType.APPLICATION_JSON)后添加   .content(JSON.toJSONString(object))代码传递该类参数;  

2.     对于get、delete方法,如果请求api的url中携带参数,如:url/demo-url/{demoId},那么请求是可以有两种写法:

       1>.    get("url/demo-url/{demoId}",demoId);

       2>.    get("url/demo-url/demoId-value"),这个demoId-value是demoId参数的真实值。

3.      在模拟方法调用时,如果调用处和被调用处传递的对象不能一致(即,需要传递的是同一个对象,但是真正调用时,可能因为反序列化等因素,得到的肯定不是原有对象(即使个属性值一样)),那么我们在模拟调用时,可以使用类似如下代码中updateDemo方法的入参代码块替代传递的对象(4种方式):

BDDMockito.when(demoService.updateDemo(org.mockito.Matchers.isA(DemoObject.class))).thenReturn(result);
BDDMockito.when(demoService.updateDemo(org.mockito.Matchers.any(DemoObject.class))).thenReturn(result);
BDDMockito.when(demoService.updateDemo(org.mockito.Matchers.any())).thenReturn(result);
BDDMockito.when(demoService.updateDemo(org.mockito.Matchers.anyObject())).thenReturn(result);

4.    在模拟方法调用时,如果调用处使用matchers的isA、any(Type.class)、any和anyObject方法充当参数时,如果有多个参数,必须所有的参数都通过matchers的方式提供,否则会报异常:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Invalid use of argument matchers!
m matchers expected, n recorded:  ……

其中,m表示有m个参数需要通过matchers(参数匹配器)方式提供,n表示只有n个参数通过该方式提供,还有m-n个也必须通过该方式提供。当然被mock方法,参数可以全为非matcher提供或全为matchers方式提供。比如:

错误:when(demo.updateDemo(null,any(Type.class))).thenReturn(result)

正确:when(demo.updateDemo(eq(null),any(Type.class))).thenReturn(result)

正确:when(demo.updateDemo(null,type)).thenReturn(result)

 

被测试方法:

@ApiOperation(value = "获得第一个demo", httpMethod = "GET", response = ResponseEntityBody.class)
@GetMapping("/demo/first")
public ResponseEntityBody findFirst() {
    ResponseEntityBody responseEntityBody = new ResponseEntityBody();

    try {
        List<Demo> list = demoService.findFirst();
        responseEntityBody.setBody(list);
        responseEntityBody.setOperationCode(OperationCode.SUCCESS);
    } catch (Exception e) {
        logger.error(e.getMessage());
    }

    return responseEntityBody;
}

 

OAuthHelper 是构造token的工具类,
 oAuthHelper.bearerToken(clientId, scopes, roles, resourceIds) 用于构造认证信息,需要自己去实现。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.stereotype.Component;
import org.springframework.test.web.servlet.request.RequestPostProcessor;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;

@Profile("test")
@Component
@EnableAuthorizationServer
public class OAuthHelper extends AuthorizationServerConfigurerAdapter {
    // 定义常量
    private static String USER_NAME = "name";

    private static String USER_PASSWORD = "psw";

    //账号可用
    private static Boolean enable = true;

    //账号不过期
    private static Boolean accountNonExpired = true;

    //凭证不过期
    private static Boolean credentialsNonExpired = true;

    //账号不锁定
    private static Boolean accountNonLocked = true;

    private static final String CLIENT_ID = "clientId";

    @Autowired
    AuthorizationServerTokenServices tokenservice;

    @Autowired
    ClientDetailsService clientDetailsService;

    public RequestPostProcessor bearerToken(final String clientId,
                                            final String[] scopes,
                                            final String[] roles,
                                            final String[] resourceIds) {
        return mockRequest -> {
            OAuth2AccessToken token = createAccessToken(clientId, scopes, roles, resourceIds);
            mockRequest.addHeader("Authorization", "Bearer " + token.getValue());
            return mockRequest;
        };
    }

    private OAuth2AccessToken createAccessToken(final String clientId,
                                                final String[] nscopes,
                                                final String[] roles,
                                                final String[] nresourceIds) {
        ClientDetails client = clientDetailsService.loadClientByClientId(clientId);

        // 生成认证信息
        Collection<GrantedAuthority> authorities = client.getAuthorities();
        if (null != roles && roles.length > 0) {
            Arrays.stream(roles).forEach(r -> authorities.add(new SimpleGrantedAuthority(r)));
        }

        // 生成资源信息
        Set<String> resourceIds = client.getResourceIds();
        if (null != nresourceIds && nresourceIds.length > 0) {
            if (resourceIds != null) {
                Arrays.stream(nresourceIds).forEach(r -> resourceIds.add(r));
            }
        }

        // 生成 scope
        Set<String> scopes = client.getScope();
        if (null != nscopes && nscopes.length > 0) {
            if (null != scopes) {
                Arrays.stream(nscopes).forEach(s -> scopes.add(s));
            }
        }

        // 生成 request
        Map<String, String> requestParameters = Collections.emptyMap();
        boolean approved = true;
        String redirectUrl = null;
        Set<String> responseTypes = Collections.emptySet();
        Map<String, Serializable> extensionProperties = Collections.emptyMap();

        OAuth2Request oAuth2Request = new OAuth2Request(requestParameters, clientId, authorities,
                approved, scopes, resourceIds, redirectUrl, responseTypes, extensionProperties);

        // 获得用户信息
        User userPrincipal = new User(USER_NAME, USER_PASSWORD, enable,
                accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);

        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(userPrincipal, null, authorities);
        OAuth2Authentication auth = new OAuth2Authentication(oAuth2Request, authenticationToken);

        return tokenservice.createAccessToken(auth);
    }

    @Override
    public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient(CLIENT_ID)
                .authorities("READ", "WRITE", "ADMIN")
        ;
    }
}

需要添加oauth2及security的相关依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <version>4.2.3.RELEASE</version>
    <scope>test</scope>
</dependency>

© 著作权归作者所有

共有 人打赏支持
李阳-kevin
粉丝 4
博文 97
码字总数 42200
作品 0
西安
程序员
在.NET下多层架构企业管理系统的开发

摘 要 随着.NET 2.0的发布,在.NET下用多层架构来构建企业管理系统也越来越方便。本文以某国外贸易公司的管理系统为背景,介绍在.NET平台下,采用多层架构构建企业管理系统。 关键词 .NET;单...

青石
2012/02/07
0
0
学习自动化测试的一些感悟

这个话题比较大,相信大家也都有自己的想法,我在这里写一些我自己的看法,请大家指教。 1、什么叫做自动化测试工程师? 首先,要会使用自动化测试工具; 接下来,对于高手来说,要能写一些独...

白一客
2017/05/25
111
0
基于 Node.js—Express 框架的博客--LittleBlog

这是一个基于 Node.js——Express 框架的个人博客。支持解析 MarkDown 并生成静态 HTML。 系统架构 特性 博客前后端分离,系统构架实现了层次化、组件化。 前端 前端通过Vue+Vuex,实现了组件...

杜小豆
2016/12/05
2.3K
3
使用JFinal进行后端开发时,如何做单元测试?

之前使用JFinal也做过一两个网站,但一直没有考虑过写单元测试,目前在考虑网站的自动化构建发布,需要引入单元测试,想请教各位大神如何做的单元测试,单元测试主要覆盖哪些部分的代码,控制...

周宇YuZhou
2016/02/27
197
1
spring boot 单元测试问题

由于我写的模块是跟第三方接口对接的底层模块,所有在写单元测试的时候只能在本地模拟了一个第三方服务器,在maven install的时候拉起了单元测试,并访问到了我的控制层,控制层会发起http的...

吖冷
04/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

活动推荐|互联网3.0与区块链新时代论坛(北京)

1 时间地点 **时间:**9月22日 14:00 - 18:00 地点:(北京海淀)西大街70号 3w咖啡 二层 2 活动详情 Harmony创始人Stephen及团队将介绍他们的区块链分片扩容技术。Stephen曾任Apple地图服务...

HiBlock
40分钟前
1
0
如何优雅的删除Redis的大key

关于Redis大键(Key),我们从[空间复杂性]和访问它的[时间复杂度]两个方面来定义大键。前者主要表示Redis键的占用内存大小;后者表示Redis集合数据类型(set/hash/list/sorted set)键,所含有的...

IT--小哥
50分钟前
1
0
spring cloud学习笔记

工具 eclipse 4.9 gradle 4.10.2 spring cloud Finchley.SR1 spring boot 2.0.4 build.gradle buildscript {ext {springBootVersion = '2.0.4.RELEASE'}repositories {jcenter()......

bobby2006
51分钟前
1
0
Tcl命令操作实验-----(5)

Vivado% proc myproc {arg} {puts $arg}Vivado% myproc mynamemynameVivado% if {2>1} {puts 2>1} else {puts 2<1}2>1...

whoisliang
今天
1
0
比特币钱包RPC的PHP调用方法

当我们希望在Php开发的网站中加入比特币支付功能时,需要解决的第一个 问题,就是如何在Php程序代码中调用比特币钱包的RPC API开发接口来实现 我们期望的功能,例如比特币的支付与接收。 例如...

汇智网教程
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部