java EE技术体系——CLF平台API开发注意事项(2)——后端测试
来源:互联网 发布:java当前类属性的类型 编辑:程序博客网 时间:2024/05/29 09:34
前言:上篇博客说到了关于开发中的一些情况,这篇博客主要说明一些关于测试的内容。
一、宏观说明
要求:每一个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开发注意事项(1)——后端开发
- java EE技术体系——CLF平台API开发注意事项(3)——API安全访问控制
- java EE技术体系——CLF平台API开发注意事项(4)——API生命周期治理简单说明
- 基于J2EE规范的中间件——Java EE技术体系
- 干货分享—Java EE企业级应用开发技术路线图
- Java技术体系的四大平台(SE ,EE,ME,Card)
- Java EE 技术 —— Servlet
- 《Java平台体系》——全书大纲
- 《Java平台体系》——前言
- 《Java平台体系》——前言
- 《Java平台体系》——第一章 Java平台概述
- 《Java平台体系》——第一章 Java平台概述
- 企业移动应用——技术平台体系
- 《Java平台体系》——学习Java平台体系的意义
- Java EE开发技术
- Java EE集群技术初探——第二部分(什么是Java EE的集群)
- Java EE集群技术初探——第八部分(关于Java EE集群的误区)
- angular2的shell命令
- 线程同步 ----互斥锁
- 表单提交多个对象
- 清华大学计算机系96级 那些缔造中国互联网的男孩们
- 学习笔记-------spring 事务控制
- java EE技术体系——CLF平台API开发注意事项(2)——后端测试
- 字符串指针与字符数组
- 接口、抽象类、普通类继承和实现关系
- MySQL索引背后的数据结构及算法原理
- 【mob】Android短信验证+源码
- 杂记
- spark程序入门-wordCount详解总结
- Balanced Lineup 线段树 rmq 区间最值
- 数据库关于varchar和nvarchar的区别