码迷,mamicode.com
首页 > 其他好文 > 详细

读书笔记-单元测试艺术(三)-使用桩对象解除依赖

时间:2016-06-06 23:29:14      阅读:302      评论:0      收藏:0      [点我收藏+]

标签:

一、几个概念

1.什么是外部依赖

外部依赖是指在系统中代码与其交互的对象,而且无法对其做人为控制。

最常见的例子是文件系统、线程、内存和时间等,我们使用桩对象来处理外部依赖问题。

2.什么是桩对象

桩对象是对系统中现有依赖的一个替代品,可人为控制。

通过使用桩对象,无需涉及依赖项,即可直接对代码进行测试。

3.什么是重构

重构是指不影响已有功能而改变代码设计的一种行为

4.什么是接缝

接缝是指代码中可以插入不同功能(如桩对象类)的地方。

二、解除依赖

抽象一个接口

namespace LogAn.Interface
{
    public interface IExtensionManager
    {
        bool IsValid(string fileName);
    }
}

实现接口的具体类

namespace LogAn.Implement
{
    public class FileExtensionManager:IExtensionManager
    {
        public bool IsValid(string fileName)
        {
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentException("No filename provided!");
            }
            if (!fileName.EndsWith(".SLF"))
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    }
}

编写一个实现该接口的桩对象类

无论文件的扩展类是什么,这个桩对象类永远返回true

public class StubExtensionManager:IExtensionManager
{
    public bool IsValid(string fileName)
    {
        return true;
    }
}

编写被测方法

现有一个接口和两个实现该接口的类,但被测类还是直接调用“真对象”;

这个时候我们需要在代码中引入接缝,以便可以使用桩对象

在被测试类中注入桩对象的实现;

在构造函数级别上接收一个接口;

namespace LogAn
{
    public class LogAnalyzer
    {
        public bool IsValidLogFileName(string fileName)
        {
            IExtensionManager mgr =new FileExtensionManager();
            return mgr.IsValid(fileName);
        }
    }
}

三、在被测类中注入桩对象-构造函数

1.重写LogAnalyzer.cs

namespace LogAn
{
    public class LogAnalyzer
    {
        private IExtensionManager manager;
        /// <summary>
        /// 在生产代码中新建对象
        /// </summary>
        public LogAnalyzer()
        {
            manager = new FileExtensionManager();
        }
        /// <summary>
        /// 定义可供测试调用的构造函数
        /// </summary>
        /// <param name="mgr"></param>
        public LogAnalyzer(IExtensionManager mgr)
        {
            manager = mgr;
        }
        public bool IsValidLogFileName(string fileName)
        {
            return manager.IsValid(fileName);
        }
    }
}
2.编写桩对象
public class StubExtensionManager : IExtensionManager
 {
     public bool ShouldExtensionBeValid;
     public bool IsValid(string fileName)
     {
         return ShouldExtensionBeValid;
     }
 }
3.编写测试方法
[TestFixture]
public class LogAnalyzerTest
{
    [Test]
    public void IsValidFileName_validFileLowerCased_ReturnTrue()
    {
        StubExtensionManager myFakeManager = new StubExtensionManager();
        myFakeManager.ShouldExtensionBeValid = true;
        LogAnalyzer analyzer = new LogAnalyzer(myFakeManager);
        bool result = analyzer.IsValidLogFileName("haha.slf");
        Assert.IsTrue(result, "filename shoud be valid!");
    }
}

4.构造函数注入方式存在的问题

如果被测代码需要多个桩对象才能正常工作,就需要增加更多的构造函数,而造成很大的困扰,甚至降低代码的可读性和可维护性

技术分享

 

5.何时使用构造函数注入方式

使用构造函数的方式,可以很好的告知API使用者:“这些参数是必须的,新建这个对象时必须传入所有参数”

如果想要这些依赖变成可选的,可以使用属性注入

四、在被测类中注入桩对象-属性注入

1.重写LogAnalyzer.cs

namespace LogAn
{
    public class LogAnalyzer
    {
        private IExtensionManager manager;
        /// <summary>
        /// 在生产代码中新建对象
        /// </summary>
        public LogAnalyzer()
        {
            manager = new FileExtensionManager();
        }

        /// <summary>
        /// 允许通过属性设置依赖
        /// </summary>
        /// <param name="mgr"></param>
        public IExtensionManager ExtensionManager
        {
            get { return manager; }
            set { manager = value; }
        }
        public bool IsValidLogFileName(string fileName)
        {
            return manager.IsValid(fileName);
        }
    }
}
2.编写桩对象类
public class StubExtensionManager : IExtensionManager
{
    public bool ShouldExtensionBeValid;
    public bool IsValid(string fileName)
    {
        return ShouldExtensionBeValid;
    }
}
3.编写测试方法
[TestFixture]
public class LogAnalyzerTest
{
    [Test]
    public void IsValidFileName_validFileLowerCased_ReturnTrue()
    {
        StubExtensionManager myFakeManager = new StubExtensionManager();
        myFakeManager.ShouldExtensionBeValid = true;
        LogAnalyzer analyzer = new LogAnalyzer();
        analyzer.ExtensionManager = myFakeManager;
        bool result = analyzer.IsValidLogFileName("haha.slf");
        Assert.IsTrue(result, "filename shoud be valid!");
    }
}

五、在被测类中注入桩对象-工厂方法

1.编写LogAnalyzer.cs

namespace LogAn
{
    public class LogAnalyzer
    {
        private IExtensionManager manager;
        /// <summary>
        /// 在生产代码中使用工厂
        /// </summary>
        /// <param name="mgr"></param>
        public LogAnalyzer()
        {
            manager = ExtensionManagerFactory.Create();
        }
        public bool IsValidLogFileName(string fileName)
        {
            return manager.IsValid(fileName);
        }
    }
}

2.编写测试方法

[TestFixture]
public class LogAnalyzerTest
{
    [Test]
    public void IsValidFileName_validFileLowerCased_ReturnTrue()
    {
        StubExtensionManager myFakeManager = new StubExtensionManager();
        myFakeManager.ShouldExtensionBeValid = true;
        //把桩对象赋给工厂类
        ExtensionManagerFactory.SetManager(myFakeManager);
        LogAnalyzer analyzer = new LogAnalyzer();
        bool result = analyzer.IsValidLogFileName("haha.slf");
        Assert.IsTrue(result, "filename shoud be valid!");
    }
}

读书笔记-单元测试艺术(三)-使用桩对象解除依赖

标签:

原文地址:http://www.cnblogs.com/2star/p/5565293.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!