博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
类依赖项的不透明性和透明性
阅读量:6476 次
发布时间:2019-06-23

本文共 5444 字,大约阅读时间需要 18 分钟。

在 TDD 的实践中,总是要考虑类的依赖项的透明性(Transparent)和不透明性(Opaque),进而采用合理的方式提高代码的可测试性。

不透明依赖

我们先看段前置条件代码,以供后文使用。

1   public interface IUserProvider 2   { 3     IList
GetUserCollection(); 4 } 5 6 public class UserProvider : IUserProvider 7 { 8 public IList
GetUserCollection() 9 {10 return new List
() 11 { 12 new User()13 {14 Name = "hello",15 LastActivity = DateTime.Now.AddDays(-1),16 },17 };18 }19 }20 21 public class User22 {23 public string Name { get; set; }24 public DateTime LastActivity { get; set; }25 }

现在,我们需要一个负责管理 User 的类 UserManager,其实现了接口 IUserManager。

1   public interface IUserManager 2   { 3     int NumberOfUsersActiveInLast10Days(string userName); 4   } 5  6   public class UserManager : IUserManager 7   { 8     public int NumberOfUsersActiveInLast10Days(string userName) 9     {10       IUserProvider userProvider = ServiceLocator.Current.GetInstance
();11 IList
userCollection = userProvider.GetUserCollection();12 int result = 0;13 foreach (User user in userCollection)14 {15 if (user.Name.StartsWith(userName)16 && user.LastActivity > DateTime.Now.AddDays(-10))17 result++;18 }19 return result;20 }21 }

通过 UserManager 内定义的 函数 NumberOfUsersActiveInLast10Days 我们可以得到过去 10 天内活跃的用户数量。

1   class Program 2   { 3     static void Main(string[] args) 4     { 5       IUnityContainer container = new UnityContainer(); 6       ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(container)); 7  8       container.RegisterType
(new ContainerControlledLifetimeManager()); 9 container.RegisterType
(new ContainerControlledLifetimeManager());10 11 UserManager userManager = new UserManager();12 int activeUserCount = userManager.NumberOfUsersActiveInLast10Days("hello");13 Console.WriteLine(activeUserCount);14 15 Console.ReadKey();16 }17 }

在函数 NumberOfUsersActiveInLast10Days 中,我们从 ServiceLocator 中获取了一个 IUserProvider 的实现,然后通过其获取所有 User。再根据给定条件过滤用户,返回过去 10 天内的活跃用户数量。

在 UserManager 的使用中,我们并不知道其依赖于 ServiceLocator 和 UserProvider 等类。

这种将 IoC 调用直接嵌入到代码实现中的隐式使用方式称之为不透明依赖注入

测试不透明依赖

现在我们来为 NumberOfUsersActiveInLast10Days 编写单元测试代码。

第一个用例为验证在数据库中不存在用户名以给定字符串开头的用户。

如果我不知道 NumberOfUsersActiveInLast10Days 的内部实现,采用黑盒测试的方式,我会写出如下代码。

1     [TestMethod] 2     public void GetActiveUsers_TestCaseOfZeroUsers_WouldReturnEmptyCollection() 3     { 4       // arrange 5       // no clear idea what to mock here 6  7       // act 8       var userManager = new UserManager(); 9       int numberOfUsers = userManager.NumberOfUsersActiveInLast10Days("x");10 11       // assert12       Assert.IsTrue(numberOfUsers == 0);13     }

则运行测试用例后,得到的结果是:

"未将对象引用设置到对象的实例。"

此时,我们知道了 NumberOfUsersActiveInLast10Days 函数还要依赖 ServiceLocator 和 UserProvider 类。

现在,我们来改进测试代码。

1     [TestMethod] 2     public void GetActiveUsers_TestCaseOfZeroUsers_WouldReturnEmptyCollection() 3     { 4       // arrange 5       IUnityContainer container = new UnityContainer(); 6       ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(container)); 7  8       IUserProvider userProvider = Substitute.For
(); 9 userProvider.GetUserCollection().Returns
>(new List
());10 container.RegisterInstance
(userProvider, new ContainerControlledLifetimeManager());11 12 // act13 var userManager = new UserManager();14 int numberOfUsers = userManager.NumberOfUsersActiveInLast10Days("x");15 16 // assert17 Assert.IsTrue(numberOfUsers == 0);18 }

则现在我们可以通过此测试了。

透明依赖

可以看到,在代码中使用不透明依赖将导致为代码编写单元测试变得困难和不可预测。

现在我来将依赖项重构为透明依赖,通过构造函数将依赖注入。

1   public class UserManager : IUserManager 2   { 3     private readonly IUserProvider _userProvider; 4  5     public UserManager(IUserProvider userProvider) 6     { 7       _userProvider = userProvider; 8     } 9 10     public int NumberOfUsersActiveInLast10Days(string userName)11     {12       IList
userCollection = _userProvider.GetUserCollection();13 int result = 0;14 foreach (User user in userCollection)15 {16 if (user.Name.StartsWith(userName)17 && user.LastActivity > DateTime.Now.AddDays(-10))18 result++;19 }20 return result;21 }22 }

代码的使用也需稍作修改。

1       UserManager userManager = new UserManager(container.Resolve
());2 int activeUserCount = userManager.NumberOfUsersActiveInLast10Days("hello");3 Console.WriteLine(activeUserCount);

这种可以明确的通过构造函数显式的注入的依赖项称之为透明依赖

测试透明依赖

改进测试代码,直接去掉了对 ServiceLocator 的依赖。

1     [TestMethod] 2     public void GetActiveUsers_TestCaseOfZeroUsers_WouldReturnEmptyCollection() 3     { 4       // arrange 5       IUserProvider userProvider = Substitute.For
(); 6 userProvider.GetUserCollection().Returns
>(new List
()); 7 8 // act 9 var userManager = new UserManager(userProvider);10 int numberOfUsers = userManager.NumberOfUsersActiveInLast10Days("x");11 12 // assert13 Assert.IsTrue(numberOfUsers == 0);14 }

这一次运行顺利的通过。

结论

通过使用透明依赖方式,可以极大的简化测试编写过程,并且可以引导更简洁的设计。同时,配合 IoC 容器的合理使用将极大的发挥依赖注入的能力。

参考资料

转载地址:http://cdmko.baihongyu.com/

你可能感兴趣的文章
python3.4学习笔记(十六) windows下面安装easy_install和pip教程
查看>>
MyGUI 解析
查看>>
Linux中的ls命令详细使用
查看>>
graph-tool文档(一)- 快速开始使用Graph-tool - 2.属性映射、图的IO和Price网络
查看>>
easyui treegrid逐步加载
查看>>
GraphicsLab Project之辉光(Glare,Glow)效果 【转】
查看>>
<转>Python: __init__.py 用法
查看>>
Linux Curl命令
查看>>
046 SparlSQL中的函数
查看>>
-27979 LoadRunner 错误27979 找不到请求表单 Action.c(73): Error -27979: Requested form not found...
查看>>
[LeetCode] Minimum Depth of Binary Tree
查看>>
,net运行框架
查看>>
Java 中 Emoji 的正则表达式
查看>>
Mixin Network第一届开发者大赛作品介绍- dodice, diceos和Fox.one luckycoin
查看>>
安卓Glide(4.7.1)使用笔记 01 - 引入项目
查看>>
中金易云:为出版社找到下一本《解忧杂货店》
查看>>
Flex布局
查看>>
Material Design之 AppbarLayout 开发实践总结
查看>>
Flutter之MaterialApp使用详解
查看>>
DataBinding最全使用说明
查看>>