重写OS查询方法以使其可测试

时间:2013-04-18 13:49:27

标签: c# unit-testing testability

我正在完成我们的应用程序的单元测试并改进/添加更多。我在单元测试/测试驱动开发中非常(不,非常)新手,我找到了我想要测试的以下方法。我被困了,我的问题是,是否有办法重写这个以便它是可测试的?

public static bool Is32BitOS()
{
        string os = (from x in new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem").Get().OfType<ManagementObject>()
                     select x.GetPropertyValue("Caption")).First().ToString().Trim();

        if (os.Equals("Microsoft Windows XP Professional"))
        {
            return true;
        }

        if (os.StartsWith("Microsoft Windows 7"))
        {
            string architecture = (from x in new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem").Get().OfType<ManagementObject>()
                                   select x.GetPropertyValue("OSArchitecture")).First().ToString();
            if (architecture == "64-bit")
            {
                return false;
            }
        }

        return true;
}

1 个答案:

答案 0 :(得分:2)

您的方法有3个职责:

  1. 管理对象搜索者创建
  2. os和架构检索
  3. os验证
  4. 为了启用测试,我将在AssemblyInfo.cs中添加一行类似于:

    的行
    [assembly: InternalsVisibleTo("Your.Test.Project")]
    

    以便受保护的方法对测试项目可见,但不能免费提供给客户。 然后我会重写你的方法,以便分离os验证:

    //Acceptance test this method
    public static bool Is32BitOS()
    {
        var managementObjects = new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem").Get().OfType<ManagementObject>();
    
        string os = (from x in managementObjects select x.GetPropertyValue("Caption")).First().ToString().Trim();
        string architecture = (from x in managementObjects select x.GetPropertyValue("OSArchitecture")).First().ToString();
    
        return Is32BitOS(os, architecture);
    }
    
    //Unit test this method
    internal static bool Is32BitOS(string os, string architecture)
    {
        if (os.Equals("Microsoft Windows XP Professional"))
        {
            return true;
        }
    
        if (os.StartsWith("Microsoft Windows 7"))
        {
            string architecture = archRetriever();
            if (architecture == "64-bit")
            {
                return false;
            }
        }
        return true;
    }
    

    现在我们已经分开了关注点,我会说你的单元测试应该只验证Is32BitOs的行为,而验收测试应该验证端到端的堆栈。 事实上,你在单元测试中没什么价值

    (from x in new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem").Get().OfType<ManagementObject>()
     select x.GetPropertyValue("Caption")).First().ToString().Trim();
    

    检索os字符串,而您的实际值在于使用此信息以确定Os是否为32位。

    在接口中包装并使用模拟框架的替代方法是应用函数编程外观here for Llewellyn Falco's peel and slice techniquehere Arlo Belshee's no mocks approach。因此,而不是像以下方法:

    public static bool Is32BitOS(IRetrieveOsAndArchitecture roa)
    {   
        string os = roa.GetOs();                
        string architecture = roa.GetArchitecure();
    
        return Is32BitOS(os, architecture);
    }
    

    您可以使用以下内容:

    public static bool Is32BitOS(Func<ManagementObjectSearcher, string> osRetriever, 
                                 Func<ManagementObjectSearcher, string> archRetriever, 
                                 ManagementObjectSearcher searcher)
    {   
        string os = osRetriever(searcher);              
        string architecture = archRetriever(searcher);
    
        return Is32BitOS(os, architecture);
    }
    

    它的客户端方法是:

    public static bool Is32BitOS()
    {
        var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem");
    
        return Is32Bit((s)=>{ (from x in s.Get().OfType<ManagementObject>() select x.GetPropertyValue("Caption")).First().ToString().Trim()},
                       (s)=>{ (from x in s.Get().OfType<ManagementObject>() select x.GetPropertyValue("OSArchitecture")).First().ToString();},
                       searcher);
    }
    

    请注意,在测试接口和功能的情况时,您没有使用真正的ManagementObjectSearcher外部依赖项;在mockist方法中,你将在函数式编程中使用一个模拟对象,你将传递一个不同的lambda表达式,它只返回os和架构字符串。

    这个方法还有一个责任,就是创建ManagementObjectSearcher;要解决此依赖关系,您可以:

    1. 将其作为方法
    2. 的参数传递
    3. 使其成为在建设时间启动的班级领域