本文首发于泊浮目的专栏: https://segmentfault.com/blog...
笔者工做2年有余,刚开始实习的时候是不知道自动化测试这种神器的,在刚开始工做的时候每每苦于救火灭火再救火,搞的心力憔悴,一度怀疑猿生。实践自动化测试后感受生产力慢慢的解放了,那个时候搞的仍是偏单机应用,测试的Cover也是止步在单机应用上。在接触到了ZStack之后,因为其产品化的特性,对软件质量要求偏高,然做为一个典型的分布式系统,测试的覆盖率倒是较高的。在这篇文章,笔者想谈谈对自动化测试的一些想法。java
自动化测试的收益点很明显,几乎众所周知:git
既然收益这么高,为何现实中自动化测试实施起来就像劳动人民爱劳动这句话同样这么不现实呢?大概有这几点:github
ZStack的自动化测试是基于Junit使用Grovvy
编写的集成测试,在运行时会把依赖的Bean按需加载进来并启动一个JVM进程,同时也会启动一个基于Jetty的HTTPServer用于Mock Agent的行为。web
不少人觉得Junit是用于作单元测试的。其实并不是如此,官网上的介绍是:JUnit is a simple framework to write repeatable tests. It is an instance of the xUnit architecture for unit testing frameworks.
package org.zstack.test.integration.kvm.vm import org.springframework.http.HttpEntity import org.zstack.header.vm.VmCreationStrategy import org.zstack.header.vm.VmInstanceState import org.zstack.header.vm.VmInstanceVO import org.zstack.kvm.KVMAgentCommands import org.zstack.kvm.KVMConstant import org.zstack.sdk.CreateVmInstanceAction import org.zstack.sdk.DiskOfferingInventory import org.zstack.sdk.ImageInventory import org.zstack.sdk.InstanceOfferingInventory import org.zstack.sdk.L3NetworkInventory import org.zstack.sdk.VmInstanceInventory import org.zstack.test.integration.kvm.Env import org.zstack.test.integration.kvm.KvmTest import org.zstack.testlib.EnvSpec import org.zstack.testlib.SubCase import org.zstack.testlib.VmSpec import org.zstack.utils.gson.JSONObjectUtil /** * Created by xing5 on 2017/2/22. */ class OneVmBasicLifeCycleCase extends SubCase { EnvSpec env def DOC = """ test a VM's start/stop/reboot/destroy/recover operations """ @Override void setup() { useSpring(KvmTest.springSpec) } @Override void environment() { env = Env.oneVmBasicEnv() } @Override void test() { env.create { testStopVm() testStartVm() testRebootVm() testDestroyVm() testRecoverVm() testDeleteCreatedVm() } } void testRecoverVm() { VmSpec spec = env.specByName("vm") VmInstanceInventory inv = recoverVmInstance { uuid = spec.inventory.uuid } assert inv.state == VmInstanceState.Stopped.toString() // confirm the vm can start after being recovered testStartVm() } void testDestroyVm() { VmSpec spec = env.specByName("vm") KVMAgentCommands.DestroyVmCmd cmd = null env.afterSimulator(KVMConstant.KVM_DESTROY_VM_PATH) { rsp, HttpEntity<String> e -> cmd = JSONObjectUtil.toObject(e.body, KVMAgentCommands.DestroyVmCmd.class) return rsp } destroyVmInstance { uuid = spec.inventory.uuid } assert cmd != null assert cmd.uuid == spec.inventory.uuid VmInstanceVO vmvo = dbFindByUuid(cmd.uuid, VmInstanceVO.class) assert vmvo.state == VmInstanceState.Destroyed } void testRebootVm() { // reboot = stop + start VmSpec spec = env.specByName("vm") KVMAgentCommands.StartVmCmd startCmd = null KVMAgentCommands.StopVmCmd stopCmd = null env.afterSimulator(KVMConstant.KVM_STOP_VM_PATH) { rsp, HttpEntity<String> e -> stopCmd = JSONObjectUtil.toObject(e.body, KVMAgentCommands.StopVmCmd.class) return rsp } env.afterSimulator(KVMConstant.KVM_START_VM_PATH) { rsp, HttpEntity<String> e -> startCmd = JSONObjectUtil.toObject(e.body, KVMAgentCommands.StartVmCmd.class) return rsp } VmInstanceInventory inv = rebootVmInstance { uuid = spec.inventory.uuid } assert startCmd != null assert startCmd.vmInstanceUuid == spec.inventory.uuid assert stopCmd != null assert stopCmd.uuid == spec.inventory.uuid assert inv.state == VmInstanceState.Running.toString() } void testStartVm() { VmSpec spec = env.specByName("vm") KVMAgentCommands.StartVmCmd cmd = null env.afterSimulator(KVMConstant.KVM_START_VM_PATH) { rsp, HttpEntity<String> e -> cmd = JSONObjectUtil.toObject(e.body, KVMAgentCommands.StartVmCmd.class) return rsp } VmInstanceInventory inv = startVmInstance { uuid = spec.inventory.uuid } assert cmd != null assert cmd.vmInstanceUuid == spec.inventory.uuid assert inv.state == VmInstanceState.Running.toString() VmInstanceVO vmvo = dbFindByUuid(cmd.vmInstanceUuid, VmInstanceVO.class) assert vmvo.state == VmInstanceState.Running assert cmd.vmInternalId == vmvo.internalId assert cmd.vmName == vmvo.name assert cmd.memory == vmvo.memorySize assert cmd.cpuNum == vmvo.cpuNum //TODO: test socketNum, cpuOnSocket assert cmd.rootVolume.installPath == vmvo.rootVolume.installPath assert cmd.useVirtio vmvo.vmNics.each { nic -> KVMAgentCommands.NicTO to = cmd.nics.find { nic.mac == it.mac } assert to != null: "unable to find the nic[mac:${nic.mac}]" assert to.deviceId == nic.deviceId assert to.useVirtio assert to.nicInternalName == nic.internalName } } void testStopVm() { VmSpec spec = env.specByName("vm") KVMAgentCommands.StopVmCmd cmd = null env.afterSimulator(KVMConstant.KVM_STOP_VM_PATH) { rsp, HttpEntity<String> e -> cmd = JSONObjectUtil.toObject(e.body, KVMAgentCommands.StopVmCmd.class) return rsp } VmInstanceInventory inv = stopVmInstance { uuid = spec.inventory.uuid } assert inv.state == VmInstanceState.Stopped.toString() assert cmd != null assert cmd.uuid == spec.inventory.uuid def vmvo = dbFindByUuid(cmd.uuid, VmInstanceVO.class) assert vmvo.state == VmInstanceState.Stopped } void testDeleteCreatedVm() { VmSpec spec = env.specByName("vm") DiskOfferingInventory diskOfferingInventory = env.inventoryByName("diskOffering") InstanceOfferingInventory instanceOfferingInventory = env.inventoryByName("instanceOffering") ImageInventory imageInventory = env.inventoryByName("image1") L3NetworkInventory l3NetworkInventory = env.inventoryByName("l3") CreateVmInstanceAction action = new CreateVmInstanceAction() action.name = "JustCreatedVm" action.rootDiskOfferingUuid = diskOfferingInventory.uuid action.instanceOfferingUuid = instanceOfferingInventory.uuid action.imageUuid = imageInventory.uuid action.l3NetworkUuids = [l3NetworkInventory.uuid] action.strategy = VmCreationStrategy.JustCreate.toString() action.sessionId = adminSession() CreateVmInstanceAction.Result result = action.call() destroyVmInstance { uuid = result.value.inventory.uuid } VmInstanceVO vo = dbFindByUuid(result.value.inventory.uuid, VmInstanceVO.class) assert vo == null } @Override void clean() { env.delete() } }
咱们先从跳转到extends的SubCase
中:spring
package org.zstack.testlib /** * Created by xing5 on 2017/2/22. */ abstract class SubCase extends Test implements Case { final void run() { try { environment() test() } catch (Throwable t) { logger.warn("a sub case [${this.class}] fails, ${t.message}", t) collectErrorLog() throw t } finally { logger.info("start cleanup for case ${this.class}") try{ clean() }catch (Throwable t){ collectErrorLog() throw t } } } @Override protected void runSubCases() { throw new Exception("runSubCases() cannot be called in a SubCase") } }
从签名中能够看到,其继承于Test
,并实现了Case
接口中的方法,咱们看一下 Case
:数据库
package org.zstack.testlib /** * Created by xing5 on 2017/3/3. */ interface Case { void environment() void test() void run() void clean() }
这里定义一个SubCase
的基本行为:json
在Test
中,咱们也能够看到定义里几个关键抽象函数,用于定义一个Case的行为:segmentfault
abstract void setup() abstract void environment() abstract void test()
因此一个Case
必须实现Test
中的接口以及Case
中的clean方法。session
通常在setup
中,会将依赖的Bean按需加载进来。这在前面提到过;而environment
则会构建出一个环境。Grovvy对DSL支持较好,因此整个环境的构建代码可读性极强,本质上每一个DSL都对应了一个Spec,而Sepc对应了一个ZStack的SDK建立调用——即XXXAction。而XXXAction则经过HTTP调用ZStack的API接口。架构
平时在测试中你们可能会为了Build一个环境直接对数据库进行操做。例如:
xxxRepo.save(new Object());
但在ZStack中并非一个很好的方案——一个Iaas中的资源依赖及状态变更的关系是错综复杂的,所以调用外部的API来建立资源是一个明智的选择。同时也能够测试SDK和API的行为是不是期待的。
在clean中也是如此。会调用ZStack自己的Cascade逻辑进行资源清理。打开EnvSpec.Grovvy
能够看到
static List deletionMethods = [ [CreateZoneAction.metaClass, CreateZoneAction.Result.metaClass, DeleteZoneAction.class], [AddCephBackupStorageAction.metaClass, AddCephBackupStorageAction.Result.metaClass, DeleteBackupStorageAction.class], [AddCephPrimaryStorageAction.metaClass, AddCephPrimaryStorageAction.Result.metaClass, DeletePrimaryStorageAction.class], [AddCephPrimaryStoragePoolAction.metaClass, AddCephPrimaryStoragePoolAction.Result.metaClass, DeleteCephPrimaryStoragePoolAction.class], [CreateEipAction.metaClass, CreateEipAction.Result.metaClass, DeleteEipAction.class], [CreateClusterAction.metaClass, CreateClusterAction.Result.metaClass, DeleteClusterAction.class], [CreateDiskOfferingAction.metaClass, CreateDiskOfferingAction.Result.metaClass, DeleteDiskOfferingAction.class], [CreateInstanceOfferingAction.metaClass, CreateInstanceOfferingAction.Result.metaClass, DeleteInstanceOfferingAction.class], [CreateAccountAction.metaClass, CreateAccountAction.Result.metaClass, DeleteAccountAction.class], [CreatePolicyAction.metaClass, CreatePolicyAction.Result.metaClass, DeletePolicyAction.class], [CreateUserGroupAction.metaClass, CreateUserGroupAction.Result.metaClass, DeleteUserGroupAction.class], [CreateUserAction.metaClass, CreateUserAction.Result.metaClass, DeleteUserAction.class], [AddImageAction.metaClass, AddImageAction.Result.metaClass, DeleteImageAction.class], [CreateDataVolumeTemplateFromVolumeAction.metaClass, CreateDataVolumeTemplateFromVolumeAction.Result.metaClass, DeleteImageAction.class], [CreateRootVolumeTemplateFromRootVolumeAction.metaClass, CreateRootVolumeTemplateFromRootVolumeAction.Result.metaClass, DeleteImageAction.class], [CreateL2NoVlanNetworkAction.metaClass, CreateL2NoVlanNetworkAction.Result.metaClass, DeleteL2NetworkAction.class], [CreateL2VlanNetworkAction.metaClass, CreateL2VlanNetworkAction.Result.metaClass, DeleteL2NetworkAction.class], [AddIpRangeByNetworkCidrAction.metaClass, AddIpRangeByNetworkCidrAction.Result.metaClass, DeleteIpRangeAction.class], [CreateL3NetworkAction.metaClass, CreateL3NetworkAction.Result.metaClass, DeleteL3NetworkAction.class], [CreateSchedulerJobAction.metaClass, CreateSchedulerJobAction.Result.metaClass, DeleteSchedulerJobAction.class], [CreateSchedulerTriggerAction.metaClass, CreateSchedulerTriggerAction.Result.metaClass, DeleteSchedulerTriggerAction.class], [CreateVmInstanceAction.metaClass, CreateVmInstanceAction.Result.metaClass, DestroyVmInstanceAction.class], [CreateDataVolumeFromVolumeSnapshotAction.metaClass, CreateDataVolumeFromVolumeSnapshotAction.Result.metaClass, DeleteDataVolumeAction.class], [CreateDataVolumeFromVolumeTemplateAction.metaClass, CreateDataVolumeFromVolumeTemplateAction.Result.metaClass, DeleteDataVolumeAction.class], [CreateDataVolumeAction.metaClass, CreateDataVolumeAction.Result.metaClass, DeleteDataVolumeAction.class], [CreateVolumeSnapshotAction.metaClass, CreateVolumeSnapshotAction.Result.metaClass, DeleteVolumeSnapshotAction.class], [AddKVMHostAction.metaClass, AddKVMHostAction.Result.metaClass, DeleteHostAction.class], [CreateLoadBalancerAction.metaClass, CreateLoadBalancerAction.Result.metaClass, DeleteLoadBalancerAction.class], [AddLocalPrimaryStorageAction.metaClass, AddLocalPrimaryStorageAction.Result.metaClass, DeletePrimaryStorageAction.class], [AddImageStoreBackupStorageAction.metaClass, AddImageStoreBackupStorageAction.Result.metaClass, DeleteBackupStorageAction.class], [AddNfsPrimaryStorageAction.metaClass, AddNfsPrimaryStorageAction.Result.metaClass, DeletePrimaryStorageAction.class], [CreatePortForwardingRuleAction.metaClass, CreatePortForwardingRuleAction.Result.metaClass, DeletePortForwardingRuleAction.class], [CreateSecurityGroupAction.metaClass, CreateSecurityGroupAction.Result.metaClass, DeleteSecurityGroupAction.class], [AddSftpBackupStorageAction.metaClass, AddSftpBackupStorageAction.Result.metaClass, DeleteBackupStorageAction.class], [AddSharedMountPointPrimaryStorageAction.metaClass, AddSharedMountPointPrimaryStorageAction.Result.metaClass, DeletePrimaryStorageAction.class], [CreateVipAction.metaClass, CreateVipAction.Result.metaClass, DeleteVipAction.class], [CreateVirtualRouterOfferingAction.metaClass, CreateVirtualRouterOfferingAction.Result.metaClass, DeleteInstanceOfferingAction.class], [CreateWebhookAction.metaClass, CreateWebhookAction.Result.metaClass, DeleteWebhookAction.class], [CreateBaremetalPxeServerAction.metaClass, CreateBaremetalPxeServerAction.Result.metaClass, DeleteBaremetalPxeServerAction.class], [CreateBaremetalChassisAction.metaClass, CreateBaremetalChassisAction.Result.metaClass, DeleteBaremetalChassisAction.class], [CreateBaremetalHostCfgAction.metaClass, CreateBaremetalHostCfgAction.Result.metaClass, DeleteBaremetalHostCfgAction.class], [CreateMonitorTriggerAction.metaClass, CreateMonitorTriggerAction.Result.metaClass, DeleteMonitorTriggerAction.class], [CreateEmailMonitorTriggerActionAction.metaClass, CreateEmailMonitorTriggerActionAction.Result.metaClass, DeleteMonitorTriggerActionAction.class], [CreateEmailMediaAction.metaClass, CreateEmailMediaAction.Result.metaClass, DeleteMediaAction.class], [AddLdapServerAction.metaClass, AddLdapServerAction.Result.metaClass, DeleteLdapServerAction.class], [SubmitLongJobAction.metaClass, SubmitLongJobAction.Result.metaClass, DeleteLongJobAction.class], ]
设置了对应的createAction和deleteAction,用于清理环境时调用。这样同时也对Cascade逻辑进行了Cover。
若是看过ZStack的Case
,能够看到不少相似的方法:
这几个方法用来hook Message和HTTP Request。因为在ZStack中各个组件的通讯都由Message来完成,对于Agent的请求则是统一经过HTTP来完成。这样在TestCase就能够任意模拟任何组件及agent的状态,让Case有极强的实用性——也保证了ManagentMent Node的逻辑健壮。
ZStack的SDK本质上是包装了一层HTTP Path,利用通用的协议便于开发者进行开发或测试。而在传统的Java WEB应用中,通常会经过MockMvc进行测试。其本质也是经过调用每一个API的Path传参来进行测试。接下来来看一个demo:
import com.camile.base.Utils.JsonUtils; import com.camile.base.common.CommonResponse; import com.camile.base.common.error.ResponseCode; import com.camile.base.common.utils.MD5Util; import com.camile.base.data.dao.UserRepository; import com.camile.base.data.dto.user.*; import com.camile.base.data.entity.UserEntity; import com.camile.base.data.vo.UserVO; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpSession; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.context.WebApplicationContext; import javax.servlet.http.HttpSession; import java.util.Map; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** * Created by Camile * 1.用户注册 * 2.用户登陆,测试本身是否处于登陆状态,并执行更新信息、修改密码操做 * 3.用户登出,更新信息、在线修改密码,应所有失败。 * 4.用户用新信息登陆,成功 * 5.用户登出,测试本身是否处于登陆状态,走忘记密码流程 * 6.修改后再次登陆,成功 */ @Slf4j @Transactional @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) public class UserBasicTests { @Autowired private UserRepository userRepository; @Autowired private WebApplicationContext context; private String uuid; private HttpSession session; private MockMvc mvc; private ObjectMapper mapper; private final String email = "487643862@qq.com"; private String password = "newPassword"; private final String question = "are you ok ?"; private final String answer = "im fine"; private final String name = "camile"; private final String phone = "13043769014"; private String updateName = "camile1"; private String updateEmail = "587643862@qq.com"; private String updatePhone = "13834671096"; @Before public void setUp() throws Exception { mvc = MockMvcBuilders.webAppContextSetup(this.context).build(); mapper = new ObjectMapper(); } @Test public void test() throws Exception { testRegisterSuccess(); testIsLoginFailure(); testLoginSuccess(); testIsLoginSuccess(); testUpdateInformationSuccess(); testOnlineRestPwdSuccess(); testLoginOutSuccess(); testUpdateInformationFailure(); testOnlineRestPwdFailure(); testloginWithOldPwdFailure(); testLoginWithNewInfoSuccess(); testLoginOutSuccess(); testForgetPwdAndResetSuccess(); testLoginWithNewInfoSuccess(); } private void testRegisterSuccess() throws Exception { UserAllPropertyDTO dto = new UserAllPropertyDTO(); dto.setEmail(email); dto.setPassword(password); dto.setQuestion(question); dto.setAnswer(answer); dto.setName(name); dto.setPhone(phone); String registerJson = JsonUtils.ObjectToJson(dto); MvcResult result = mvc.perform(MockMvcRequestBuilders.post("/user/register.do") .contentType(MediaType.APPLICATION_JSON) .content(registerJson) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); String content = result.getResponse().getContentAsString(); CommonResponse response = mapper.readValue(content, CommonResponse.class); Assert.assertEquals(response.getCode(), ResponseCode.Success.getCode()); UserVO vo = JsonUtils.jsonToObject((Map) response.getData(), UserVO.class); Assert.assertNotNull(userRepository.findByUuid(vo.getUuid())); uuid = vo.getUuid(); session = result.getRequest().getSession(); } private void testIsLoginFailure() throws Exception { // never login MvcResult result = mvc.perform(MockMvcRequestBuilders.get(String.format("/user/isLogin.do?uuid=%s", uuid)) .session((MockHttpSession) session) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); String content = result.getResponse().getContentAsString(); CommonResponse response = mapper.readValue(content, CommonResponse.class); Assert.assertEquals(response.getCode(), ResponseCode.NeedLogin.getCode()); session = result.getRequest().getSession(); } private void testLoginSuccess() throws Exception { UserLoginDTO dto = new UserLoginDTO(name, password); String loginJson = JsonUtils.ObjectToJson(dto); MvcResult result = mvc.perform(MockMvcRequestBuilders.post("/user/login.do") .session((MockHttpSession) session) .contentType(MediaType.APPLICATION_JSON) .content(loginJson) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); String content = result.getResponse().getContentAsString(); CommonResponse response = mapper.readValue(content, CommonResponse.class); Assert.assertEquals(response.getCode(), ResponseCode.Success.getCode()); session = result.getRequest().getSession(); } private void testIsLoginSuccess() throws Exception { MvcResult result = mvc.perform(MockMvcRequestBuilders.get(String.format("/user/isLogin.do?uuid=%s", uuid)) .session((MockHttpSession) session) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); String content = result.getResponse().getContentAsString(); CommonResponse response = mapper.readValue(content, CommonResponse.class); Assert.assertEquals(ResponseCode.Success.getCode(), response.getCode()); session = result.getRequest().getSession(); } private void testUpdateInformationSuccess() throws Exception { UserDTO dto = new UserDTO(); dto.setUuid(uuid); dto.setName(updateName); dto.setEmail(updateEmail); dto.setPhone(updatePhone); String updateJson = JsonUtils.ObjectToJson(dto); MvcResult result = mvc.perform(MockMvcRequestBuilders.put("/user/information.do") .session((MockHttpSession) session) .contentType(MediaType.APPLICATION_JSON) .content(updateJson) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); String content = result.getResponse().getContentAsString(); CommonResponse response = mapper.readValue(content, CommonResponse.class); Assert.assertEquals(response.getMsg(), ResponseCode.Success.getCode(), response.getCode()); UserEntity entity = userRepository.findByUuid(uuid); UserVO vo = JsonUtils.jsonToObject((Map) response.getData(), UserVO.class); Assert.assertNotNull(entity); Assert.assertEquals(vo.getName(), entity.getName()); Assert.assertEquals(vo.getPhone(), entity.getPhone()); Assert.assertEquals(vo.getEmail(), entity.getEmail()); Assert.assertEquals(vo.getEmail(), updateEmail); Assert.assertEquals(vo.getPhone(), updatePhone); Assert.assertEquals(vo.getName(), updateName); session = result.getRequest().getSession(); } private void testOnlineRestPwdSuccess() throws Exception { UserResetPwdDTO dto = new UserResetPwdDTO(); dto.setUuid(uuid); dto.setOldPassword(password); dto.setNewPassword("12345678"); password = "12345678"; String resetPwdJson = JsonUtils.ObjectToJson(dto); MvcResult result = mvc.perform(MockMvcRequestBuilders.post("/user/onlineResetPwd.do") .session((MockHttpSession) session) .contentType(MediaType.APPLICATION_JSON) .content(resetPwdJson) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); String content = result.getResponse().getContentAsString(); CommonResponse response = mapper.readValue(content, CommonResponse.class); Assert.assertEquals(response.getMsg(), response.getCode(), ResponseCode.Success.getCode()); session = result.getRequest().getSession(); UserEntity userEntity = userRepository.findByUuid(uuid); Assert.assertEquals(userEntity.getPassword(), MD5Util.MD5EncodeUtf8(password)); } private void testLoginOutSuccess() throws Exception { MvcResult result = mvc.perform(MockMvcRequestBuilders.post(String.format("/user/loginOut.do?uuid=%s", uuid)) .session((MockHttpSession) session) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); String content = result.getResponse().getContentAsString(); CommonResponse response = mapper.readValue(content, CommonResponse.class); Assert.assertEquals(response.getCode(), ResponseCode.Success.getCode()); session = result.getRequest().getSession(); } private void testUpdateInformationFailure() throws Exception { String updateName = "camile2"; String updateEmail = "687643862@qq.com"; String updatePhone = "14834671096"; UserDTO dto = new UserDTO(); dto.setUuid(uuid); dto.setName(updateName); dto.setEmail(updateEmail); dto.setPhone(updatePhone); String updateJson = JsonUtils.ObjectToJson(dto); MvcResult result = mvc.perform(MockMvcRequestBuilders.put("/user/information.do") .session((MockHttpSession) session) .contentType(MediaType.APPLICATION_JSON) .content(updateJson) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); String content = result.getResponse().getContentAsString(); CommonResponse response = mapper.readValue(content, CommonResponse.class); Assert.assertEquals(response.getMsg(), ResponseCode.Failure.getCode(), response.getCode()); session = result.getRequest().getSession(); } private void testOnlineRestPwdFailure() throws Exception { UserResetPwdDTO dto = new UserResetPwdDTO(); dto.setUuid(uuid); dto.setOldPassword(password); dto.setNewPassword("123456789"); String resetPwdJson = JsonUtils.ObjectToJson(dto); MvcResult result = mvc.perform(MockMvcRequestBuilders.post("/user/onlineResetPwd.do") .session((MockHttpSession) session) .contentType(MediaType.APPLICATION_JSON) .content(resetPwdJson) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); String content = result.getResponse().getContentAsString(); CommonResponse response = mapper.readValue(content, CommonResponse.class); Assert.assertEquals(response.getMsg(), response.getCode(), ResponseCode.Failure.getCode()); session = result.getRequest().getSession(); UserEntity userEntity = userRepository.findByUuid(uuid); Assert.assertNotEquals(userEntity.getPassword(), MD5Util.MD5EncodeUtf8("123456789")); } private void testloginWithOldPwdFailure() throws Exception { UserLoginDTO dto = new UserLoginDTO(name, "newPassword"); String loginJson = JsonUtils.ObjectToJson(dto); MvcResult result = mvc.perform(MockMvcRequestBuilders.post("/user/login.do") .session((MockHttpSession) session) .contentType(MediaType.APPLICATION_JSON) .content(loginJson) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); String content = result.getResponse().getContentAsString(); CommonResponse response = mapper.readValue(content, CommonResponse.class); Assert.assertEquals(response.getCode(), ResponseCode.UserInfoError.getCode()); session = result.getRequest().getSession(); } private void testLoginWithNewInfoSuccess() throws Exception { UserLoginDTO dto = new UserLoginDTO(updateName, password); String loginJson = JsonUtils.ObjectToJson(dto); MvcResult result = mvc.perform(MockMvcRequestBuilders.post("/user/login.do") .session((MockHttpSession) session) .contentType(MediaType.APPLICATION_JSON) .content(loginJson) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); String content = result.getResponse().getContentAsString(); CommonResponse response = mapper.readValue(content, CommonResponse.class); Assert.assertEquals(ResponseCode.Success.getCode(), response.getCode()); session = result.getRequest().getSession(); } private void testForgetPwdAndResetSuccess() throws Exception { MvcResult result = mvc.perform(MockMvcRequestBuilders.get(String.format("/user/forget/question?name=%s", updateName)) .session((MockHttpSession) session) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); String content = result.getResponse().getContentAsString(); CommonResponse response = mapper.readValue(content, CommonResponse.class); Assert.assertEquals(response.getMsg(), ResponseCode.Success.getCode(), response.getCode()); session = result.getRequest().getSession(); String question = (String) response.getData(); Assert.assertEquals(question, this.question); UserQuestionDTO dto = new UserQuestionDTO(); dto.setName(updateName); dto.setQuestion(question); dto.setAnswer(answer); String questionJson = JsonUtils.ObjectToJson(dto); result = mvc.perform(MockMvcRequestBuilders.post("/user/forget/checkAnswer.do") .session((MockHttpSession) session) .contentType(MediaType.APPLICATION_JSON) .content(questionJson) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); content = result.getResponse().getContentAsString(); response = mapper.readValue(content, CommonResponse.class); Assert.assertEquals(ResponseCode.Success.getCode(), response.getCode()); session = result.getRequest().getSession(); String token = (String) response.getData(); UserForgetResetPwdDTO userForgetResetPwdDTO = new UserForgetResetPwdDTO(); userForgetResetPwdDTO.setForgetToken(token); userForgetResetPwdDTO.setName(updateName); userForgetResetPwdDTO.setNewPassword("superpwd!"); password = "superpwd!"; String resetPwdDTO = JsonUtils.ObjectToJson(userForgetResetPwdDTO); result = mvc.perform(MockMvcRequestBuilders.post("/user/forget/resetPassword.do") .session((MockHttpSession) session) .contentType(MediaType.APPLICATION_JSON) .content(resetPwdDTO) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); content = result.getResponse().getContentAsString(); response = mapper.readValue(content, CommonResponse.class); Assert.assertEquals(response.getMsg(), ResponseCode.Success.getCode(), response.getCode()); session = result.getRequest().getSession(); UserEntity userEntity = userRepository.findByUuid(uuid); Assert.assertEquals(userEntity.getPassword(), MD5Util.MD5EncodeUtf8(password)); } }
咱们能够看到MockMvc
的链式调用让代码可读性变得极强:
MvcResult result = mvc.perform(MockMvcRequestBuilders.post("/user/register.do") .contentType(MediaType.APPLICATION_JSON) .content(registerJson) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn();
在这里,咱们对MockMvc的对象设置了相应的URL以及Content类型、数据,而且期待了它的状态码。
在这篇文章中,笔者和你们一块儿分析了ZStack的自动化测试,以及在JavaWeb应用中常见的测试方法。固然,这些测试都属于集成测试。而单元测试以及如何在本身的应用中编写一套更强大的自动测试框架这类主题,以后有机会笔者会再与你们分享。
扩展阅读: ZStack:管理节点基于模拟器的Integration Test框架