.NET程序员项目开发必知必会—Dev环境中的集成测试用例执行时上下文环境检查(实战)
Microsoft.NET 解决方案,项目开发必知必会。
从这篇文章开始我将分享一系列我认为在实际工作中很有必要的一些.NET项目开发的核心技术点,所以我称为必知必会。尽管这一系列是使用.NET/C#来展现,但是同样适用于其他类似的OO技术平台,这些技术点可能称不上完整的技术,但是它是经验的总结,是掉过多少坑之后的觉醒,所以有必要花几分钟时间记住它,在真实的项目开发中你就知道是多么的有帮助。好了,废话不说了,进入主题。
我们在开发服务时为了调试方便会在本地进行一个基本的模块测试,你也可以认为是集成测试,只不过你的测试用例不会覆盖到80%以上,而是一些我们认为在开发时不是很放心的点才会编写适当的用例来测试它。
集成测试用例通常有多个执行上下文,对于我们开发人员来说我们的执行上下文通常都在本地,测试人员的上下文在测试环境中。开发人员的测试用来是不能够连接到其他环境中去的(当然视具体情况而定,有些用例很危险是不能够乱连接的,本文会讲如何解决),开发人员运行的集成测试用例所要访问的所有资源、服务都是在开发环境中的。这里依然存在但是,但是为了调试方便,我们还是需要能够在必要的时候连接到其他环境中去调试问题,为了能够真实的模拟出问题的环境、可真实的数据,我们需要能有一个这样的机制,在需要的时候我能够打开某个设置让其能够切换集成测试运行的环境上下文,其实说白了就是你所要连接的环境、数据源的连接地址。
本篇文章我们将通过一个简单的实例来了解如何简单的处理这中情况,这其实基于对测试用来不断重构后的效果。
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting; namespace OrderManager.Test
{
using ProductService.Contract; /// <summary>
/// Product service integration tests.
/// </summary>
[TestClass]
public class ProductServiceIntegrationTest
{
/// <summary>
/// service address.
/// </summary>
public const string ServiceAddress = "http://dev.service.ProductService/"; /// <summary>
/// Product service get product by pid test.
/// </summary>
[TestMethod]
public void ProductService_GetProductByPid_Test()
{
var serviceInstance = ProductServiceClient.CreateClient(ServiceAddress);
var testResult = serviceInstance.GetProductByPid(); Assert.AreNotEqual(testResult, null);
Assert.AreEqual(testResult.Pid, );
}
}
}
这是一个实际的集成测试用例代码,有一个当前测试类共用的服务地址,这个地址是DEV环境的,当然你也可以定义其他几个环境的服务地址,前提是环境是允许你连接的,那才有实际意义。
我们来看测试用例,它是一个查询方法测试用例,用来对ProductServiceClient.GetProductByPid服务方法进行测试,由于面向查询的操作是等幕的,不论我们查询多少次这个ID的Product,都不会对数据造成影响,但是如果我们测试的是一个更新或者删除就会带来问题。
在DEV环境中,测试更新、删除用例没有问题,但是如果你的机器是能够连接到远程某个生产或者PRD测试上时会带来一定的危险性,特别是在忙的时候,加班加点的干进度,你很难记住你当前的机器的host配置中是否还连接着远程的生产机器上,或者根本就不需要配置host就能够连接到某个你不应该连接的环境上。
这是目前的问题,那么我们如何解决这个问题呢 ,我们通过对测试代码进行一个简单的重构就可以避免由于连接到不该连接的环境中运行危险的测试用例。
其实很多时候,重构真的能够帮助我们找到出口,就好比俗话说的:"出口就在转角处“,只有不断重构才能够逐渐的保证项目的质量,而这种效果是很难得的。
提取抽象基类,对测试要访问的环境进行明确的定义。
namespace OrderManager.Test
{
public abstract class ProductServiceIntegrationBase
{
/// <summary>
/// service address.
/// </summary>
protected const string ServiceAddressForDev = "http://dev.service.ProductService/"; /// <summary>
/// service address.
/// </summary>
protected const string ServiceAddressForPrd = "http://Prd.service.ProductService/"; /// <summary>
/// service address.
/// </summary>
protected const string ServiceAddressTest = "http://Test.service.ProductService/";
}
}
对具体的测试类消除重复代码,加入统一的构造方法。
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting; namespace OrderManager.Test
{
using ProductService.Contract; /// <summary>
/// Product service integration tests.
/// </summary>
[TestClass]
public class ProductServiceIntegrationTest : ProductServiceIntegrationBase
{
/// <summary>
/// product service client.
/// </summary>
private ProductServiceClient serviceInstance; /// <summary>
/// Initialization test instance.
/// </summary>
[TestInitialize]
public void InitTestInstance()
{
serviceInstance = ProductServiceClient.CreateClient(ServiceAddressForDev/*for dev*/);
} /// <summary>
/// Product service get product by pid test.
/// </summary>
[TestMethod]
public void ProductService_GetProductByPid_Test()
{
var testResult = serviceInstance.GetProductByPid(); Assert.AreNotEqual(testResult, null);
Assert.AreEqual(testResult.Pid, );
} /// <summary>
/// Product service delete search index test.
/// </summary>
[TestMethod]
public void ProductService_DeleteProductSearchIndex_Test()
{
var testResult = serviceInstance.DeleteProductSearchIndex(); Assert.IsTrue(testResult);
}
}
}
消除重复代码后,我们需要加入对具体测试用例检查是否能够连接到某个环境中去。我加入了一个DeleteProductSearchIndex测试用例,该用例是用来测试删除搜索索引的,这个测试用例只能够在本地DEV环境中运行(你可能觉得这个删除接口不应该放在这个服务里,这里只是举一个例子,无需纠结)。
为了能够有一个检查机制能提醒开发人员你目前连接的地址是哪一个,我们需要借助于测试上下文。
重构后,我们看一下现在的测试代码结构。
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting; namespace OrderManager.Test
{
using ProductService.Contract; /// <summary>
/// Product service integration tests.
/// </summary>
[TestClass]
public class ProductServiceIntegrationTest : ProductServiceIntegrationBase
{
/// <summary>
/// product service client.
/// </summary>
private ProductServiceClient serviceInstance; /// <summary>
/// Initialization test instance.
/// </summary>
[TestInitialize]
public void InitTestInstance()
{
serviceInstance = ProductServiceClient.CreateClient(ServiceAddressForPrd/*for dev*/); this.CheckCurrentTestCaseIsRun(this.serviceInstance);//check current test case .
} /// <summary>
/// Product service get product by pid test.
/// </summary>
[TestMethod]
public void ProductService_GetProductByPid_Test()
{
var testResult = serviceInstance.GetProductByPid(); Assert.AreNotEqual(testResult, null);
Assert.AreEqual(testResult.Pid, );
} /// <summary>
/// Product service delete search index test.
/// </summary>
[TestMethod]
public void ProductService_DeleteProductSearchIndex_Test()
{
var testResult = serviceInstance.DeleteProductSearchIndex(); Assert.IsTrue(testResult);
}
}
}
我们加入了一个很重要的测试实例运行时方法InitTestInstance,该方法会在测试用例每次实例化时先执行,在方法内部有一个用来检查当前测试用例运行的环境
this.CheckCurrentTestCaseIsRun(this.serviceInstance);//check current test case .,我们转到基类中。
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting; namespace OrderManager.Test
{
public abstract class ProductServiceIntegrationBase
{
/// <summary>
/// service address.
/// </summary>
protected const string ServiceAddressForDev = "http://dev.service.ProductService/"; /// <summary>
/// get service address.
/// </summary>
protected const string ServiceAddressForPrd = "http://Prd.service.ProductService/"; /// <summary>
/// service address.
/// </summary>
protected const string ServiceAddressTest = "http://Test.service.ProductService/"; /// <summary>
/// Test context .
/// </summary>
public TestContext TestContext { get; set; } /// <summary>
/// is check is run for current test case.
/// </summary>
protected void CheckCurrentTestCaseIsRun(ProductService.Contract.ProductServiceClient testObject)
{
if (testObject.ServiceAddress.Equals(ServiceAddressForPrd))// Prd 环境,需要小心检查
{
if (this.TestContext.TestName.Equals("ProductService_DeleteProductSearchIndex_Test"))
Assert.IsTrue(false, "当前测试用例连接的环境为PRD,请停止当前用例的运行。");
}
else if (testObject.ServiceAddress.Equals(ServiceAddressTest))//Test 环境,检查约定几个用例
{
if (this.TestContext.TestName.Equals("ProductService_DeleteProductSearchIndex_Test"))
Assert.IsTrue(false, "当前测试用例连接的环境为TEST,为了不破坏TEST环境,请停止用例的运行。");
}
}
}
}
在检查方法中我们使用简单的判断某个用例不能够在PRD、TEST环境下执行,虽然判断有点简单,但是在真实的项目中足够了,简单有时候是一种设计思想。我们运行所有的测试用例,查看各个状态。

一目了然,更为重要的是它不会影响你对其他用例的执行。当你在深夜12点排查问题的时候,你很难控制自己的眼花、体虚导致的用例执行错误带来的大问题,甚至是无法挽回的的错误。
此文献给那些跟我一样的.NET程序员们,通过简单的重构,我们解放了自己。
.NET程序员项目开发必知必会—Dev环境中的集成测试用例执行时上下文环境检查(实战)的更多相关文章
- 迈向高阶:优秀Android程序员必知必会的网络基础
1.前言 网络通信一直是Android项目里比较重要的一个模块,Android开源项目上出现过很多优秀的网络框架,从一开始只是一些对HttpClient和HttpUrlConnection简易封装使用 ...
- Android程序员必知必会的网络通信传输层协议——UDP和TCP
1.点评 互联网发展至今已经高度发达,而对于互联网应用(尤其即时通讯技术这一块)的开发者来说,网络编程是基础中的基础,只有更好地理解相关基础知识,对于应用层的开发才能做到游刃有余. 对于Android ...
- .NET零基础入门09:SQL必知必会
一:前言 仿佛到了更进一步的时候了,每一个程序员迟早都会遇到数据存储的问题.我们拿什么来存储程序产生的数据?举例来说,用什么来存储我们的打老鼠游戏每次的成绩呢?选择如下: 1:内存中.缺点,退出游戏, ...
- 2015 前端[JS]工程师必知必会
2015 前端[JS]工程师必知必会 本文摘自:http://zhuanlan.zhihu.com/FrontendMagazine/20002850 ,因为好东东西暂时没看懂,所以暂时保留下来,供以 ...
- [ 学习路线 ] 2015 前端(JS)工程师必知必会 (2)
http://segmentfault.com/a/1190000002678515?utm_source=Weibo&utm_medium=shareLink&utm_campaig ...
- H5系列之History(必知必会)
H5系列之History(必知必会) 目录 概念 兼容性 属性 方法 H5方法 概念 理解History Api的使用方式 目的是为了解决哪些问题 作用:ajax获取数据时 ...
- 学习《SQL必知必会(第4版)》中文PDF+英文PDF+代码++福达BenForta(作者)
不管是数据分析还是Web程序开发,都会接触到数据库,SQL语法简洁,使用方式灵活,功能强大,已经成为当今程序员不可或缺的技能. 推荐学习<SQL必知必会(第4版)>,内容丰富,文字简洁明快 ...
- 读《MySQL必知必会》我学到了什么?
前言 最近在写项目的时候发现自己的SQL基本功有些薄弱,遂上知乎查询MYSQL关键字,期望得到某些高赞答案的指点,于是乎发现了 https://www.zhihu.com/question/34840 ...
- 第4节:Java基础 - 必知必会(中)
第4节:Java基础 - 必知必会(中) 本小节是Java基础篇章的第二小节,主要讲述抽象类与接口的区别,注解以及反射等知识点. 一.抽象类和接口有什么区别 抽象类和接口的主要区别可以总结如下: 抽象 ...
随机推荐
- JS详细教程(下)
五.数组 数组是数据的有序列表,每个元素在数组中都有数字位置编号,也就是索引.JS中的数组是弱类型,每一项都可以保存任何类型的数据. 创建数组 ①使用Array构造函数 var arr=new Arr ...
- Oracle数据库资源管理
1.了解Resource Manager术语 2.了解Resource Manager分配方法 3.了解DEFAULT_PLAN 4.新建资源计划 5.创建使用者组 6.了解资源分配方法 7.分配使用 ...
- hibernate笔记--单(双)向的多对多映射关系
在讲单向的多对多的映射关系的案例时,我们假设我们有两张表,一张角色表Role,一张权限表Function,我们知道一个角色或者说一个用户,可能有多个操作权限,而一种操作权限同时被多个用户所拥有,假如我 ...
- ++a和a++的区别。
先来看2段js代码 var a=0; var b=0; while(a<10) { document.write(a++); } document .write("<br> ...
- 一步一步开发Game服务器(二)完成登陆,聊天
我知道这样的文章在博客园已经多的大家都不想看了,但是这是我的系列文章开始,请各位大神见谅了. 多线程,线程执行器,(详见),socket通信相关 (详见) 本人blog相关文章测试代码,示例,完整版s ...
- NSPredicate 的使用
NSPredicate是什么? NSPredicate 是预测的意思 但是我们常翻译成谓词 它可以干什么? 使用NSPredicate可以定义模糊查询条件 根据一定的条件 我们就可以从一个数组中快速找 ...
- React Native at first sight
what is React Native? 跟据官方的描述, React Native是一套使用 React 构建 Native app 的编程框架. 推出不久便引发了广泛关注, 这也得益于 Java ...
- java.lang.OutOfMemoryError:GC overhead limit exceeded填坑心得
我遇到这样的问题,本地部署时抛出异常java.lang.OutOfMemoryError:GC overhead limit exceeded导致服务起不来,查看日志发现加载了太多资源到内存,本地的性 ...
- asp.net MVC 回顾 Html.ActionLink
在asp.net MVc中想生成一个超链接有很多种方式,通过直接输入<a>.Html.ActionLink.Html.RouteLink等等,今天我们要阐述的就是Html.ActionLi ...
- asp.net的快捷实用分页类
KeleyiPager分页类,可以于对列表页进行分页浏览,代码是从HoverTreeCMS项目中COPY的,感觉很不错,使用简单方便,但是功能强大. 在线体验效果:http://cms.hovertr ...