由单元测试想到的应用TDD产生的优势——对象之间的低耦合设计
来源:互联网 发布:spss软件安装 编辑:程序博客网 时间:2024/06/07 17:31
最近在看单元测试的文章,看到一个比较好的例子,即由单元测试的可测试性来判断对象之间的耦合程度,顺便印证了一把TDD(Test-Driven Development)测试驱动开发理念的优势——大大降低了对象之间的耦合度。下面请看具体例子吧。
单元测试中有一个比较重要的概念即可测试性,指的是是否可以对目标对象进行独立的测试,所谓独立测试即要隔绝内外部的相关联系,如链接数据库、读取文本、与另一个对象通信等。先举一个紧耦合的例子说明可测试性的失败,由于对象之间的紧耦合关系:
假设现在有一个用户名和密码验证服务,首先它需要从某个数据库或文件读取用户名和密码,并与外部输入的用户名和密码进行hash比对,上代码:
UserValidation类:
public class UserValidation { public bool CheckAuth(string userId,string userPwd) { //get user password by id from database AccountDao accountDao = new AccountDao(); string passwordByaccountDao = accountDao.GetPassword(userId); //hash userPwd from external HashUtil hashUtil = new HashUtil(); string hashRstByuserPwd = hashUtil.GetHashResult(userPwd); //Check two password are equal or not equal return passwordByaccountDao == hashRstByuserPwd; } }
AccountDao类:(链接数据库获取密码)
public class AccountDao { public string GetPassword(string id) { throw new NotImplementedException(); } }
HashUtil类:(计算外部传入的密码哈希值)
public class HashUtil { public string GetHashResult(string pwd) { throw new NotImplementedException(); } }
以上类之间的关系图如下:
CheckAuth方法的单元测试如下:
[TestMethod()] public void CheckAuthTest() { //arrange UserValidation target = new UserValidation(); string userId = string.Empty; string userPwd = string.Empty; bool expected = false; bool actual; //act actual = target.CheckAuth(userId, userPwd); //assert Assert.AreEqual(expected, actual); }
UserValidation类的CheckAuth方法直接依赖于AccountDao类和HashUtil类,导致编写针对CheckAuth方法时必须链接数据库和与HashUtil的通信,这样就违背了单元测试的基本规则——与外部对象隔离进行测试,这样的测试实际上已经是集成测试了,即访问了数据库,又计算了密码的hash值,而CheckAuth真正的核心商业逻辑应该是这样的:
1、获取根据userId对应的用户密码值,数据如何获取,它不必关心;
2、针对外部传入的userPwd进行hash运算,获取密码的hash值,如何进行hash运算,它也不必关心;
3、对1、2步骤的获取的值进行对比,返回对比的结果。
根据以上新的商业逻辑,我们可以修改上述代码如下:
第一步:提取接口IAccountDao
interface IAccountDao { string GetPassword(string id); }
第二步:提取接口IHashUtil
interface IHashUtil { string GetHashResult(string pwd); }
第三步:修改UserValidation类如下:
public class UserValidation { private IAccountDao accountDao; private IHashUtil hashUtil; public UserValidation(IAccountDao accountDao,IHashUtil hashUtil) { this.accountDao = accountDao; this.hashUtil = hashUtil; } public bool CheckAuth(string userId,string userPwd) { //get user id and password from database string passwordByaccountDao = this.accountDao.GetPassword(userId); //hash userPwd from external string hashRstByuserPwd = this.hashUtil.GetHashResult(userPwd); //Check two password are equal or not equal return passwordByaccountDao == hashRstByuserPwd; } }
以上类的关系如下所示:
通过这样的重构,单元测试代码可按照隔离原则编写如下:
[TestMethod()] public void CheckAuthTest() { //arrange IAccountDao accountDao = null; IHashUtil hashUtil = null; UserValidation target = new UserValidation(accountDao,hashUtil); string userId = string.Empty; string userPwd = string.Empty; bool expected = false; bool actual; //act actual = target.CheckAuth(userId, userPwd); //assert Assert.AreEqual(expected, actual); }
当然经过以上的代码编写,单元测试还是无法通过的,因为输入没有,下面引入stub桩对象实现输入模拟:
桩对象StubAccountDao只要实现IAccountDao接口即可满足要求,代码如下:
public class StubAccountDao:IAccountDao { public string GetPassword(string id) { return "66"; } }
桩对象StubHashUtil实现IHashUtil接口代码如下:
public class StubHashUtil:IHashUtil { public string GetHashResult(string pwd) { return "66"; } }
单元测试代码修改如下:
[TestMethod()] public void CheckAuthTest() { //arrange IAccountDao accountDao =new StubAccountDao(); IHashUtil hashUtil = new StubHashUtil(); UserValidation target = new UserValidation(accountDao,hashUtil); string userId = string.Empty; string userPwd = string.Empty; bool expected = true; bool actual; //act actual = target.CheckAuth(userId, userPwd); //assert Assert.AreEqual(expected, actual); }
至此,本次单元测试编写完成,从重构失败的产品代码开始,到引入桩对象完成程序输入模拟,最终通过单元测试。
最后,为什么说对TDD是一种印证呢?如果大家反过来先写单元测试代码,会不会直接就定义接口,使用松耦合的设计来编写产品代码呢?
- 由单元测试想到的应用TDD产生的优势——对象之间的低耦合设计
- 由设计架构想到的....
- 由班车线路调整想到的:高内聚低耦合
- 对象之间的耦合性
- 低耦合高内聚 原则的应用
- 低耦合高内聚 原则的应用
- 由结构体设计想到的
- 由Android缓存设计想到的
- TDD是如何解耦合的?
- 降低对象之间的耦合关系
- 由本子想到的
- 由忧郁想到的
- 由电车男想到的
- 由MBV想到的
- 由“枣子”想到的
- 由 setMeidatime() 想到的
- 由ff想到的
- 由HelloWorld想到的
- 微信端网页的部署步骤
- C语言代码评审小结
- 使用httpClient MultipartRequestEntity文件上传解析文件和普通表单参数
- gitlab使用--创建一个新项目
- android-banner项目的使用
- 由单元测试想到的应用TDD产生的优势——对象之间的低耦合设计
- Yum\Apt\Emerge和PortsLinux包管理系统的命令对照
- ajax
- 对于Android的插件化(功能拆分成插件,按需加载)的了解
- Gym 101164.F
- 海量数据实时在线分析Quick BI入门
- postgresql 的三种日志
- 精通算法系列-三值更小
- JS实现web页面的导航栏时间与本地同步,实时更新!