使用包装类模拟.NET类

时间:2011-03-27 06:43:30

标签: c# .net unit-testing

我有一个类,它接受一个MethodInfo实例并从中提取一些信息,但我想模拟这个类。目前很难,因为它需要一个MethodInfo,所以我的计划是为MethodInfo类创建一个包装器并在其上实现一个接口。例如:

public interface IMethodInfo
{
    string Name { get; }
}

public class MethodInfoProxy : IMethodInfo
{
    private readonly MethodInfo _method;
    public MethodInfoProxy(MethodInfo method)
    {
        _method = method;
    }

    public string Name { get { return _method.Name; } }
}

public class MyClass
{
    public MyClass(IMethodInfo method)
    {
        ...
    }
}

另一个例子是File.Exists方法。我们的想法是创建一个IFile.Exists并将它放在一个FileProxy类上,该类只需委托给File.Exists。

由于我是整个单元测试世界的新手,我想知道这是不是一个好的方法?

5 个答案:

答案 0 :(得分:3)

这里有两个选项:

  1. 使用可以模拟静态和密封类的模拟框架,如Microsoft MolesTypeMock Isolator。这很好,因为您最终不会更改代码只是为了将测试中的代码与其依赖项隔离开来。
  2. 为要模拟的行为定义接口,然后创建包装静态调用或其他难以测试的api的默认实现。这是你建议的方法,也是我经常使用的方法。在定义这些接口时,关键是通过某种形式的dependency injection将接口的实/模实现传递给测试类 - 通常是构造函数注入。有些人犯了在被测试的类中构造对象的错误,这使得无法进行测试。一个好的经验法则是,当你看到在你的业务代码中构造对象时,那就是代码味道 - 并不总是坏事,但绝对是怀疑的东西。有关于这些内容的精彩视频:The Clean Code Talks - "Global State and Singletons"
  3. 那些认为测试不应该改变代码的人和认为应该改变代码的人之间会发生一些宗教战争。依赖注入,如果您要通过创建接口进行模拟,这是必不可少的,这会导致代码具有高内聚和松散耦合以及直观的API。但另一种方法并没有排除这些好处 - 它只是不那么自动。

答案 1 :(得分:1)

我建议尝试将依赖项从类中提取出来 - 而不是提供MethodInfo(或代理),只需提供Name

如果不切实际,你可以编写使用适配器接口的代理类(如你所建议的)或使用像BlackMick或Moles这样的黑魔法工具(只是开玩笑说黑魔法部分:我只是对他们没有任何经验。)

如果您打算使用代理方法,请务必查看SystemWrapper库,该库已经处理了.NET框架中的大约20个类。

答案 2 :(得分:0)

您可以在您使用的每个类周围创建一个包装器,但这样会非常昂贵。最好使用模拟框架,例如Microsoft http://research.microsoft.com/en-us/projects/pex/的moles框架,它也可以存根静态方法。

答案 3 :(得分:0)

Mock类(或假类)将是一个用来满足依赖关系的类,并通过排除依赖项中的问题使测试更具确定性。

public interface IMethodInfo
{
    string Name { get; }
}

你的模拟课程:

FakeMethodInfo : IMethodInfo
{
 string Name {get {return "FakeMethod";}}
}

现在,在您的单元测试中,将FakeMethodInfo课程传递到您需要IMethodInfo的位置。

整个目的是你知道FakeMethodInfo只返回一个字符串,所以如果出现问题,不是问题。


不知道MethodInfo在您的程序中有什么上下文。一个更好的例子是

interface IUser
{
 string UserName {get;}
}

如果它是在一个类中实现的,那么你将从数据库中获得实际的用户名。

现在,如果你制作一个假的,并传递它,你有点模拟没有真实用户登录的用户,你排除任何问题都与`IUser。

看到我发布的这个大回答你为什么要使用嘲笑。以Moq为例:

Difference between Dependency Injection and Mocking framework (Ninject vs RhinoMock or Moq)

答案 4 :(得分:0)

为了更快地创建包装类,您可以使用我创建的Scaffold代码生成器之一。

https://www.nuget.org/packages/Digitrish.WrapperGenerator/

这将生成可用于任何模拟框架和实际实现的具体包装类的接口。