[英]unit test dynamic loading code
我正在阅读,发现此代码是对问题的质疑
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;
}
我想知道对代码进行单元测试的最佳方法是什么?
通过这种方式重构:
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;
}
我将内部循环体提取为一个方法。 我将努力使该方法返回T实例,如果它未通过测试,则返回null。 现在,我可以编写单元测试了。
if (interfaceType.IsAssignableFrom(type) && interfaceType != type)
return (T)Activator.CreateInstance(type);
else
return null;
现在,我可以提供这些新的函数类型(我希望为其返回非null实例)和一些类型(希望它们返回null)。 其余所有代码似乎都在使用系统调用,我倾向于相信这些。 但是,如果您不这样做,请单独进行测试。 测试GetFiles()
为您提供正确的文件列表; 测试GetTypes()
在给定文件中为您提供正确的类型,等等。
在一种方法中完成了三件无关的事情(请参阅SRP )。 它们中的每一个都应该分离到各自的类中,该类实现一些接口,以便您可以模拟它们以提高可测试性。 树上的东西是:
从插件目录中找到.dll文件。
加载.dll并获取其包含的类型。 这应该是调用API方法的单一代码。 您实际上并不需要进行测试(至少不需要在单元测试中进行测试),因为您可以合理地假设编程语言的库可以正常工作。
创建插件类型的实例。
将算法分为这三个部分时,您可以单独对第1部分和第3部分进行单元测试(尽管从技术上讲,第1部分的测试不是单元测试,因为它涉及文件系统,除非C#有某种方法可以模拟文件系统,例如Java 7的NIO2文件系统API应该是可模拟的)。 您还可以通过模拟第2部分来对将它们组合在一起的代码进行单元测试。
我将以某种方式抽象出动态程序集的检测和加载,以使我可以模拟该零件并将其加载为单元测试的一部分。
或者,由于您可以指定目录,因此只需在计算机的TEMP目录中构造一个临时目录(作为单元测试代码的一部分),然后将单个程序集从单元测试项目复制到该程序集,然后要求插件系统进行扫描该目录。
您没有说要使用哪个单元测试框架,因此这里假定Visual Studio的内置功能。
您需要将有问题的程序集添加到部署项列表中,以便将它们复制到单元测试工作目录中。
我看到的问题是在所有不同的实现上运行单元测试。 在单个测试中测试所有实现将使不清楚哪些实现失败。 您希望为每个实现运行一次测试。
但是,您可能会滥用数据驱动的测试机制来执行此操作。 您可以在加载所有x实现(在Class_Initialize或其他方式)后,在一些临时数据库表中插入数字0 ... x-1。 为每个测试设置该表,测试将运行x次,并以数字作为输入数据。 使用它作为implementations
列表的索引(存储在成员变量中),然后对该实现进行测试。
是的,非常丑陋..并且您失去了进行实际数据驱动测试的能力,而又没有增加更多丑陋感。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.