我正在阅读并发现此代码作为问题的答案
public List<T> LoadPlugin<T>(string directory)
{
Type interfaceType = typeof(T);
List<T> implementations = new List<T>();
//TODO: perform checks to ensure type is valid
foreach (var file in System.IO.Directory.GetFiles(directory))
{
//TODO: add proper file handling here and limit files to check
//try/catch added in place of ensure files are not .dll
try
{
foreach (var type in System.Reflection.Assembly.LoadFile(file).GetTypes())
{
if (interfaceType.IsAssignableFrom(type) && interfaceType != type)
{
//found class that implements interface
//TODO: perform additional checks to ensure any
//requirements not specified in interface
//ex: ensure type is a class, check for default constructor, etc
T instance = (T)Activator.CreateInstance(type);
implementations.Add(instance);
}
}
}
catch { }
}
return implementations;
}
它让我想知道对这段代码进行单元测试的最佳方法是什么?
答案 0 :(得分:4)
通过这种方式重构:
public List<T> LoadPlugin<T>(Type[] types)
{
Type interfaceType = typeof(T);
List<T> implementations = new List<T>();
//TODO: perform checks to ensure type is valid
try
{
foreach (var type in types)
{
if (interfaceType.IsAssignableFrom(type) && interfaceType != type)
{
//found class that implements interface
//TODO: perform additional checks to ensure any
//requirements not specified in interface
//ex: ensure type is a class, check for default constructor, etc
T instance = (T)Activator.CreateInstance(type);
implementations.Add(instance);
}
}
}
catch { }
return implementations;
}
答案 1 :(得分:1)
我会将内部循环体提取到方法中。我努力让该方法返回T实例,如果测试失败则返回null。现在我可以编写单元测试了。
if (interfaceType.IsAssignableFrom(type) && interfaceType != type)
return (T)Activator.CreateInstance(type);
else
return null;
现在我可以提供这些新函数类型,我期望返回非空实例,以及我希望返回null的类型。所有其余的代码似乎都在使用系统调用,我倾向于相信这些。但如果你不这样做 - 那就单独测试那些。测试GetFiles()
为您提供正确的文件列表;测试GetTypes()
为您提供给定文件中的正确类型等。
答案 2 :(得分:1)
在这一种方法中完成了三个不相关的事情(参见SRP)。它们中的每一个都应该分离到它们自己的类,它实现了一些接口,这样你就可以模拟它们以获得更好的可测试性。树的东西是:
从插件目录中找出.dll文件。
加载.dll并获取其包含的类型。这应该是一个调用API方法的单行程序。你真的不需要测试它(至少不是在单元测试中),因为你可以合理地假设编程语言的库工作正常。
创建插件类型的实例。
当算法被分成这三个部分时,你可以单独测试第1部分和第3部分(虽然技术上第1部分的测试不是单元测试,因为它接触文件系统,除非C#有某种方式来模拟文件系统,如Java 7的NIO2文件系统API应该是可模拟的)。您还可以通过模拟第2部分来单元测试将它们放在一起的代码。
答案 3 :(得分:0)
我会抽象出动态程序集的检测和加载,以便我可以模拟该部分并加载测试程序集作为单元测试的一部分。
或者,既然您可以指定一个目录,只需构建一个临时目录作为单元测试代码的一部分,在计算机的TEMP目录中,将单个程序集从单元测试项目复制到它,然后询问插件系统扫描该目录。
答案 4 :(得分:0)
您没有说明您正在使用哪个单元测试框架,因此我将假设Visual Studio的内置功能。</ p>
您需要将有问题的程序集添加到部署项列表中,以便将它们复制到单元测试工作目录中。
我看到的问题是在所有不同的实现上运行单元测试。在单个测试中测试所有实现将使得不清楚哪个实现失败。您希望为每个实现运行一次测试。
然而,您可能会滥用数据驱动的测试机制来执行此操作。在加载所有x实现(在Class_Initialize或其他)之后,您可以在某些临时数据库表中插入数字0 ... x-1。
为每个测试设置该表,测试将运行x次,数字作为输入数据。将其用作implementations
列表的索引(存储在成员变量中)并对该实现运行测试。
是的,非常难看..你失去了进行实际数据驱动测试的能力,而不会增加更多的丑陋。