java EE技术体系——CLF平台API开发注意事项(2)——后端测试
前言:上篇博客说到了关于开发中的一些情况,这篇博客主要说明一些关于测试的内容。
一、宏观说明
要求:每一个API都必须经过测试。 备注:如果涉及到服务间调用(如权限和基础数据),而对方服务不可用时,马上索取对方服务API,自行构建mock service(嘿嘿,小伙伴们都懂得,咱家做mock service的速度很快哈)
工具:Arquillian 备注:和以往测试使用JUnit不同,本平台项目测试使用Auquillian框架。 简单了解Arquillian:http://www.infoq.com/cn/articles/dan-allen-arquillian-framework
本平台已经集成了Arquillian相应的配置,大家直接在测试包里面添加自己的测试类即可,如果需要了解从无到有的配置,请参考:http://arquillian.org/guides/getting_started/?utm_source=cta
基本使用情况:本测试,完全模拟客户端的调用轨迹,进行包含业务在内的测试。所以,接下来需要重点说明已经配置好的两个基本类和相关文件。
二、细节说明
2.1,抽象类,组装测试环境
package com.dmsdbj.library.controller; import com.dmsdbj.library.controller.util.HeaderUtil;
import com.dmsdbj.library.repository.AbstractRepository;
import com.dmsdbj.library.repository.producer.EntityManagerProducer;
import com.dmsdbj.library.repository.producer.LoggerProducer;
import java.io.File;
import java.net.URL;
import java.util.Map;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.ArchivePaths;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.ClassLoaderAsset;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.shrinkwrap.resolver.api.maven.Maven;
import org.jboss.shrinkwrap.resolver.api.maven.MavenResolverSystem;
import org.junit.Before;
import org.junit.runner.RunWith; /**
* Abstract class for base application packaging.
*
*/
@RunWith(Arquillian.class)
public abstract class AbstractTest { @ArquillianResource
private URL deploymentUrl;
private WebTarget webTarget;
protected final static MavenResolverSystem RESOLVER = Maven.resolver(); public static WebArchive buildArchive() {
File[] jacksonFiles = RESOLVER.resolve("com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:2.7.5").withTransitivity().asFile();
File[] deltaspikeFiles = RESOLVER.resolve("org.apache.deltaspike.core:deltaspike-core-api:1.5.0").withTransitivity().asFile();
File[] deltaspikeImplFiles = RESOLVER.resolve("org.apache.deltaspike.core:deltaspike-core-impl:1.5.0").withTransitivity().asFile(); final WebArchive archive = ShrinkWrap.create(WebArchive.class);
archive.addClass(AbstractRepository.class).addPackage(HeaderUtil.class.getPackage())
.addClass(EntityManagerProducer.class).addClass(LoggerProducer.class)
.addAsLibraries(jacksonFiles).addAsLibraries(deltaspikeFiles).addAsLibraries(deltaspikeImplFiles)
.addAsWebInfResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml"))
.addAsResource("test-persistence.xml", "META-INF/persistence.xml")
.addAsResource(new ClassLoaderAsset("META-INF/sql/insert.sql"), "META-INF/sql/insert.sql")
.setWebXML("web.xml");
return archive;
} @Before
public void buildWebTarget() throws Exception {
webTarget = ClientBuilder.newClient().target(deploymentUrl.toURI().toString() + "resources/");
} protected Invocation.Builder target(String path) {
return webTarget.path(path).request();
} protected Invocation.Builder target(String path, Map<String, Object> params) {
WebTarget target = webTarget.path(path);
for (String key : params.keySet()) {
if (path.contains(String.format("{%s}", key))) {
target = target.resolveTemplate(key, params.get(key));
} else {
target = target.queryParam(key, params.get(key));
}
}
return target.request();
} }
2.2,抽象类,模拟客户端调用环境
package com.dmsdbj.library.controller; import com.dmsdbj.library.app.config.ConfigResource;
import com.dmsdbj.library.app.config.Constants;
import com.dmsdbj.library.app.security.SecurityUtils;
import com.dmsdbj.library.app.security.jwt.JWTAuthenticationFilter;
import com.dmsdbj.library.app.service.mail.MailService;
import com.dmsdbj.library.app.util.RandomUtil;
import com.dmsdbj.library.app.service.UserService;
import com.dmsdbj.library.controller.dto.LoginDTO;
import com.dmsdbj.library.controller.dto.UserDTO;
import com.dmsdbj.library.entity.AbstractAuditingEntity;
import com.dmsdbj.library.entity.AuditListner;
import com.dmsdbj.library.entity.Authority;
import com.dmsdbj.library.entity.User;
import com.dmsdbj.library.repository.AuthorityRepository;
import com.dmsdbj.library.repository.UserRepository;
import static com.dmsdbj.library.controller.AbstractTest.buildArchive;
import java.util.Map;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.core.Response;
import org.jboss.shrinkwrap.api.asset.ClassLoaderAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.After;
import org.junit.Before; /**
* Abstract class for application packaging.
*
*/
public abstract class ApplicationTest extends AbstractTest { protected static final String USERNAME = "admin";
protected static final String PASSWORD = "admin";
protected static final String INVALID_PASSWORD = "invalid_password";
protected static final String INCORRECT_PASSWORD = "pw";
private static final String AUTH_RESOURCE_PATH = "api/authenticate"; protected String tokenId; public static WebArchive buildApplication() {
return buildArchive().addPackages(true, ConfigResource.class.getPackage(), MailService.class.getPackage(), UserDTO.class.getPackage(), SecurityUtils.class.getPackage(), RandomUtil.class.getPackage())
.addClass(User.class).addClass(Authority.class).addClass(AbstractAuditingEntity.class).addClass(AuditListner.class)
.addClass(UserRepository.class).addClass(AuthorityRepository.class).addClass(UserService.class)
.addAsResource(new ClassLoaderAsset("config/application.properties"), "config/application.properties")
.addAsResource(new ClassLoaderAsset("i18n/messages.properties"), "i18n/messages.properties")
.addClass(UserJWTController.class).addPackage(JWTAuthenticationFilter.class.getPackage());
} @Before
public void setUp() throws Exception {
login(USERNAME, PASSWORD);
} @After
public void tearDown() {
logout();
} protected Response login(String username, String password) {
LoginDTO loginDTO = new LoginDTO();
loginDTO.setUsername(username);
loginDTO.setPassword(password);
Response response = target(AUTH_RESOURCE_PATH).post(Entity.json(loginDTO));
tokenId = response.getHeaderString(Constants.AUTHORIZATION_HEADER);
return response;
} protected void logout() {
tokenId = null;
} @Override
protected Invocation.Builder target(String path) {
return super.target(path).header(Constants.AUTHORIZATION_HEADER, tokenId);
} @Override
protected Invocation.Builder target(String path, Map<String, Object> params) {
return super.target(path, params).header(Constants.AUTHORIZATION_HEADER, tokenId);
} }
注意:这两个抽象类,在测试的时候,唯一可能会需要修改的,是用户名和密码。修改场景:测试API的权限;如createAccount(User user)的权限为admin,那么这里可能需要更改其他角色,以测试权限访问的准确性!
2.3,具体测试类
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.dmsdbj.library.controller; import static com.dmsdbj.library.controller.ApplicationTest.buildApplication;
import com.dmsdbj.library.entity.TOpinion;
import com.dmsdbj.library.repository.TOpinionRepository;
import java.util.ArrayList;
import static java.util.Collections.singletonMap;
import java.util.List;
import javax.inject.Inject;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import static org.hamcrest.CoreMatchers.is;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.InSequence;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import static org.valid4j.matchers.http.HttpResponseMatchers.hasContentType;
import static org.valid4j.matchers.http.HttpResponseMatchers.hasStatus; /**
*
* @author Angelina
*/
public class OpinionControllerTest extends ApplicationTest { @Deployment
public static WebArchive createDeployment() {
return buildApplication().addClass(TOpinion.class).addClass(TOpinionRepository.class).addClass(OpinionController.class);
} private static TOpinion opinion; @Inject
private TOpinionRepository TOpinionRepository; @Test
@InSequence(1)
public void findOpinionByStatus() throws Exception {
List<String> status=new ArrayList<>();
status.add("待解决");
int databaseSize = TOpinionRepository.exampleForNameQuery2(status).size();
Response response = target("/opinion/findByStatus",singletonMap("status","待解决")).get();
assertThat(response, hasStatus(Response.Status.OK));
assertThat(response, hasContentType(MediaType.APPLICATION_JSON_TYPE)); List<TOpinion> Opinion;
Opinion= response.readEntity(List.class);
assertThat(Opinion.size(),is(databaseSize));
} }
注意:
1,这里面的WebArchive方法,是组建业务调用线的。目前的方法说明:controller依赖repository,逐次依赖entity。在我们真是开发项目结构中,我们有service层,所以,测试的时候,需要再repository后面,加上service依赖!
2,严禁使用System.out.println去进行测试,必须使用断言assertThat
3,每个方法需要进行的测试项:状态码,消费类型,数据格式,权限。具体实际,严格按照API文档执行测试!
4,在测试包中,有一个TUserControllerTest的测试类,这个类是一个测试模板,对于delete、put、post、get等方式,以及各种传参、各种返回值类型的写法
三、总结
虽然咱们没有用Junit,但Arquillian是基于其再次封装。对于咱们来说,没有任何的学习成本和难度,所以,心态放平,一切都是so easy。对于新事物,应该保持一种激情!
对于咱们的安全控制,目前的规范是java EE中Security,实现的是OAuth2.0协议,采用的模式是简化模式。 具体情况,我找时间,再分享!
java EE技术体系——CLF平台API开发注意事项(2)——后端测试的更多相关文章
- java EE技术体系——CLF平台API开发注意事项(4)——API生命周期治理简单说明
文档说明 截止日期:20170905,作者:何红霞,联系方式:QQ1028335395.邮箱:hehongxia626@163.com 综述 有幸加入到javaEE技术体系的研究与开发,也得益于大家的 ...
- java EE技术体系——CLF平台API开发注意事项(1)——后端开发
前言:这是一篇帮助小伙伴在本次项目中快速进入到java EE开发的一些说明,为了让同组小伙伴们开发的时候,有个清晰点的思路.昨天给大家演示分享了基本概况,但没有留下文字总结说明,预防后期有人再次问我, ...
- java EE技术体系——CLF平台API开发注意事项(3)——API安全访问控制
前言:提离职了,嗯,这么多年了,真到了提离职的时候,心情真的很复杂.好吧,离职阶段需要把一些项目中的情况说明白讲清楚,这篇博客就简单说一下在平台中对API所做的安全处理(后面讲网关还要说,这里主要讲代 ...
- [置顶] 遵循Java EE标准体系的开源GIS服务平台架构
传送门 ☞ 系统架构设计 ☞ 转载请注明 ☞ http://blog.csdn.net/leverage_1229 传送门 ☞ GoF23种设计模式 ☞ 转载请注明 ☞ http://blog.csd ...
- [置顶] 遵循Java EE标准体系的开源GIS服务平台之二:平台部署
传送门 ☞ 系统架构设计 ☞ 转载请注明 ☞ http://blog.csdn.net/leverage_1229 传送门 ☞ GoF23种设计模式 ☞ 转载请注明 ☞ http://blog.csd ...
- Java对接拼多多开放平台API(加密上云等全流程)
前言 本文为[小小赫下士 blog]原创,搬运请保留本段,或请在醒目位置设置原文地址和原作者. 作者:小小赫下士 原文地址:Java对接拼多多开放平台API(加密上云等全流程) 本文章为企业ERP(I ...
- Spring 4 官方文档学习 Spring与Java EE技术的集成
本部分覆盖了以下内容: Chapter 28, Remoting and web services using Spring -- 使用Spring进行远程和web服务 Chapter 29, Ent ...
- 揭秘Java架构技术体系
Web应用,最常见的研发语言是Java和PHP. 后端服务,最常见的研发语言是Java和C/C++. 大数据,最常见的研发语言是Java和Python. 可以说,Java是现阶段中国互联网公司中,覆盖 ...
- 微信开放平台API开发资料
微信大概两年前开启了微信公众平台的API供开发者使用,从账号登陆.消息发送.用户账号管理.公众号菜单.客服接口.微信商店接口.用户卡券接口 以及微信支付接口.可以说是全方面覆盖了电商所需要的要素,与阿 ...
随机推荐
- Selenium3+webdriver学习笔记2(常用元素定位方式,定位单个元素共8种,总共有23种)
#!/usr/bin/env python# -*- coding:utf-8 -*- from selenium import webdriver import time,os # about:ad ...
- 新手第一天学习 C#语言(进制互换)
说起来我们对一些陌生或者未知的东西有一些恐惧感,但是又有一些期待,虽然我不确定自己能不能学会这门语言,但是我会尽自己最大的努力学. 我们第一天学的的内容呢,对大多数的人都知道,计算机的语言是二进制,但 ...
- 使用代码获得Netweaver里某个software component和C4C的版本
有同事问如何通过代码的方式获得Netweaver里某个Software component的版本信息,以及Cloud for Customer(C4C)的版本信息. Netweaver 点了Detai ...
- 在openSUSE 13.1上用gem安装rails无反应: gem install rails
解决方案: gem install rails -V ....其实他本身在后台运行,白白的给他中断好多次,用-V这个选项就可以直接回显信息了
- (外挂破解)Cheat Engine(内存修改工具)V6.2中文版软件介绍
Heat Engine是一款内存修改编辑工具,Cheat Engine允许你修改你的游戏,所以你将总是赢.它包括16进制编辑,反汇编程序,内存查找工具.与同类修改工具相比,它具有强大的反汇编功能,且自 ...
- VB SMTP用户验证发送mail
转自 http://www.jishuzh.com/program/vb-smtp%E7%94%A8%E6%88%B7%E9%AA%8C%E8%AF%81%E5%8F%91%E9%80%81mail. ...
- Spark集锦
1 Spark官网 http://spark.apache.org/ 2 Spark书籍 http://down.51cto.com/tag-spark%E4%B9%A6%E7%B1%8D.html
- 关于SpringMVC注解
1.@RequestMapping RequestMapping是一个用来处理请求地址映射的注解(将请求映射到对应的控制器方法中),可用于类或方法上.用于类上,表示类中的所有响应请求的方法都是以该地址 ...
- Shell脚本调用ftp上传文件
Shell脚本调用ftp上传文件 1.脚本如下 ftp -n<<! open x.x.x.x ###x.x.x.x为ftp地址 user username password ###user ...
- console.log与console.dir的区别
今天学习promise的时候看到了console.dir这个方法,感到很好奇,查了以下感觉又长知识了 在Chrome中,控制台对象定义了两个似乎做同样事情的方法: console.log() cons ...