外部依赖是指在系统中代码与其交互的对象,而且无法对其做人为控制。(如文件系统,线程,内存和时间等)
桩对象是对系统中现有依赖项的一个替代品,可人为控制,通过使用桩对象,无需涉及依赖项,即可直接对代码进行测试。
下面通过例子演示使用桩对象处理外部依赖问题。
1.抽取接口,以允许替换底层实现
定义接口IExtensionManager
public interface IExtensionManager
{
bool IsValid(string fileName);
}
定义实现类FileExtensionManager
public class FileExtensionManager : IExtensionManager
{
public bool IsValid(string fileName)
{
if (string.IsNullOrEmpty(fileName))
throw new ArgumentException("fileName is null");
if (!fileName.ToLower().EndsWith(".log"))
{
return false;
}
return true;
}
}
2.在被测类中注入桩对象
public class StubExtensionManager : IExtensionManager
{
public bool ShouldExtensionBeValid;
public bool IsValid(string fileName)
{
return ShouldExtensionBeValid;
}
}
3.通过构造函数注入桩对象
public class LogAnalyzer
{
private const int minFileNameLength = 6;
private IExtensionManager manager;
//生产环境中构造对象
public LogAnalyzer()
{
manager = new FileExtensionManager();
}
//测试调用的构造函数
public LogAnalyzer(IExtensionManager mgr)
{
manager = mgr;
}
//被测试方法
//支持文件类型及文件名是否足够长
public bool IsValidLogFileName(string fileName)
{
if (manager.IsValid(fileName) && Path.GetFileNameWithoutExtension(fileName).Length > minFileNameLength)
return true;
return false;
}
}
4.测试代码
[TestFixture]
public class LogAnalyzerTests
{
[Test]
public void IsValidLogFileName_NameShorterThan6charsButSupportedExtension_ReturnFalse()
{
//创建桩对象
StubExtensionManager fakeManager = new StubExtensionManager();
fakeManager.ShouldExtensionBeValid = true;
//新建analyzer并注入桩对象
LogAnalyzer analyzer = new LogAnalyzer(fakeManager);
bool result = analyzer.IsValidLogFileName("s.log");
Assert.IsFalse(result, "File name with less than 6 chars should have failed the method, even if the extension is supported.");
}
}