如何在C#中的方法内创建模拟实例?

时间:2014-04-21 17:18:07

标签: c# unit-testing mocking

所以这是我的课程的简化版本。我有两个实现服务接口的类。一个是我的实际实现,一个是我的模拟 我需要在Worker类中创建此服务类的X个新实例。

如何告诉我的工人类我想使用哪种IService?
嘲笑工人阶级是唯一的路线吗?有更好的路线吗?

修改 因此,在阅读了一些回复之后,我觉得我没有准确地表达我的情况。我过分简化到了阻碍的地步。无论如何,我添加了更多代码,可能有助于解释我发生了什么。

我将原始的Worker课程更改为Manager,并且拥有Workers的子列表。工作列表是从Manager类创建的,我使用IService构建它们,我想通过一个具体的OR Mock实现。然后我将开始工作人员做一些涉及IService的事情以及我提供的变量。 (在这种情况下,它只是一个整数,但在现实生活中它是一个类)。

public interface IService
{
    int a { get; set; }
}

public class Concrete : IService
{
    public int a { get; set; }
    float x;

    public Concrete(int A)
    {
        a = A;
        x = 0.500F;
    }
}

public class Mock : IService
{
    public int a { get; set; }
    float x;

    public Mock(int A)
    {
        a = A;
        x = 0.750F;
    }
}

public class Manager
{
    List<Worker> Workers = new List<Worker>();

    void CreateList() 
    {
        for (int i = 1; i <= 3; i++)
        {
            Worker w = new Worker(new IService(i)); //<-- Need help here!
            theList.Add(w);
        }

        KickOffWorkesAsync();
    }

    void KickOffWorkesAsync()
    {
        // process the workers async
    }

}

public class Worker
{
    IService _iService;

    public Worker(IService _iSer)
    {
        _iService = _iSer;
    }

    void DoSomething() 
    {
        //do work with _iService
    }
}

3 个答案:

答案 0 :(得分:2)

这是Dependency Injection派上用场的地方。您需要设计您的类以从外部获取它们的依赖关系,而不是自己创建它们。

类似的东西:

public class Worker
{
  public Worker(IService service) 
  {
    this.service = service;
  }

  public void DoSomething()
  {
    DoSomethingWith(service);
  }
}

然后在测试中,您可以创建一个注入了模拟服务的Worker实例,而在生产代码中,您将使用实际服务。

使用依赖注入时,有助于使用DI容器(有时也称为控制容器的反转)来正确连接相关服务,而不是手动创建依赖关系图。一些常见的DI容器用于.NET包括NinjectStructureMapCastle Windsor和Microsoft的Unity

如果您之前从未使用过DI,那么这是一个相当典型的转变,但它本质上是一个简单的改变,可以为您的代码设计带来奇迹(原因很多,不是只是为了测试)。

答案 1 :(得分:2)

另一种方法是为worker提供一个工厂方法来创建服务:

public class Worker
{
    private Func<int, IService> createService;

    public Worker(Func<int, IService> createService)
    {
        this.createService = createService;
    }

然后工人可以这样做:

void DoSomething() 
{
    var theList = new List<IService>();
    for (int i = 1; i <= 3; i++)
    {
        IService s = createService(i); // Invoke the provided factory method
        theList.Add(s);
    }

    // do something with the list...
}

并且单元测试可以像这样创建工作者:

public void SomeTestMethod()
{
    var worker = new Worker(CreateMockService);
}

private IService CreateMockService(int arg)
{
    return new Mock(arg);
}

生产代码将提供创建实际服务的工厂方法。

答案 2 :(得分:0)

尽可能简单地回答你的问题。

  void DoSomething() 
  {
      var theList = new List<IService>();
      for (int i = 1; i <= 3; i++)
      {
          //use this
          IService s = new Concrete(i); 
          //or this
          //IService s = new new Mock(i);
          theList.Add(s);
      }

      // do something with the list...
  }

但更好的方法是使用某种类型的依赖注入或工厂(如其他答案所解释的),因此您可以在配置文件或应用启动中指定您想要的IService使用