繁体   English   中英

单元测试动态加载代码

[英]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 )。 它们中的每一个都应该分离到各自的类中,该类实现一些接口,以便您可以模拟它们以提高可测试性。 树上的东西是:

  1. 从插件目录中找到.dll文件。

  2. 加载.dll并获取其包含的类型。 这应该是调用API方法的单一代码。 您实际上并不需要进行测试(至少不需要在单元测试中进行测试),因为您可以合理地假设编程语言的库可以正常工作。

  3. 创建插件类型的实例。

将算法分为这三个部分时,您可以单独对第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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM