桩破除依赖2-构造函数注入
在构造函数层注入一个伪对象(构造函数注入)
这个方法需要给被测试类添加一个新的构造函数,或给已有的构造函数添加一个新的参数,传入一个之前抽取出来的接口(IExtensionManager)类型对象。然后在被测试类中添加一个这个接口类型的局部字段,把传入的对象付给这个局部字段,供被测试方法或其他方法使用。 ClassUnderTest(IExtensionManager mgr) { m_manager = mgr } IExtensionManager m_manager; IsValidFileName(string) { if(m_manager.isvalid(file)) .... } 以下是构造注入的代码 class LogAnalyzerConstructorInject { //定义局部字段 private IExtensionManager manager; //定义测试代码可以调用的构造函数 public LogAnalyzerConstructorInject(IExtensionManager mgr) { manager = mgr; } public bool IsValidLogFileName(string fileName) { //使用构造函数传入的IExtensionManager类 return manager.IsValid(fileName); } } public interface IExtensionManager { bool IsValid(string fileName); } [TestFixture] public class LogAnalyzerTests { [Test] public void IsValidFileName_NameSupportedExtension_ReturnsTrue() { //准备一个返回true的桩 FakeExtensionManager myFakeManager = new FakeExtensionManager(); myFakeManager.WillBeValid = true; //传入桩 LogAnalyzerConstructorInject log = new LogAnalyzerConstructorInject(myFakeManager); bool result = log.IsValidLogFileName("short.ext"); Assert.True(result); } } //定义一个最简单的桩 internal class FakeExtensionManager : IExtensionManager { public bool WillBeValid = false; public bool IsValid(string fileName) { return WillBeValid; } } 使用构造函数注入伪对象可能会带来问题:
用伪对象模拟异常首先给之前的FakeExtensionManager增加抛出异常的桩,然后再添加测试代码,注入代码中。 如果需要测试通过,就需要在被测试代码 //测试抛出异常的情况 [Test] public void IsValidFileName_ExtManagerThrowsException_ReturnFalse() { FakeExtensionManager myFakeManager = new FakeExtensionManager(); myFakeManager.WillThrow = new Exception("This is fake"); LogAnalyzerConstructorInject log = new LogAnalyzerConstructorInject(myFakeManager); bool result = log.IsValidLogFileName("anything.anyextension"); Assert.False(result); } //定义一个最简单的桩,具有返回true、false和抛出异常的功能 internal class FakeExtensionManager : IExtensionManager { //模拟返回true或false用的,需要测试时赋值 public bool WillBeValid = false; //模拟返回异常的,需要测试时赋值 public Exception WillThrow = null; public bool IsValid(string fileName) { if (WillThrow != null) { throw WillThrow; } return WillBeValid; } } 增加了try-catch的被测代码 class LogAnalyzerConstructorInject { //定义局部字段 private IExtensionManager manager; //定义测试代码可以调用的构造函数 public LogAnalyzerConstructorInject(IExtensionManager mgr) { manager = mgr; } public bool IsValidLogFileName(string fileName) { ////使用构造函数传入的IExtensionManager类 //return manager.IsValid(fileName); //给上面代码增加try-catch块 try { return manager.IsValid(fileName); } catch { return false; } } } 使用构造函数注入的时机如果想告诉API的使用者某些参数是必须的,在构造函数中使用这些参数是一个极好的办法,这些参数必须在创建对象时传入。 而如果想让这些依赖成为可选项,就可以使用属性注入的方法。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |