如何在不向用户公开接口的情况下使用Moq进行模拟?

时间:2013-09-07 11:00:24

标签: c# unit-testing moq

假设我有一个这样的课程:

public class Context {
  public void initialize() { ... }
}

和另一个使用它的课程:

public class Service {
  public Context getContext() { return context; }

  internal Service(Context ctx) {
    context = ctx;
    context.initialize();
  }

  private Context context;
}

最后,假设我想为Service编写单元测试,以验证在构建类时调用Context.initalize。由于它是一个单元测试,我想模拟Context而不是使用真正的类。使用Moq需要创建一个接口IContext并重写代码如下:

public interface IContext {
  void initialize();
}

public class Context : IContext {
  public void initialize() { ... }
}

public class Service {
  public IContext getContext() { return context; }

  internal Service(IContext ctx) {
    context = ctx;
    context.initialize();
  }

  private IContext context;
}

[TestFixture()]
public class ServiceTest {
  [Test()]
  public shouldInitializeContextWhenConstructed() {
    Mock<IContext> mockContext = new Mock<IContext>();
    Service service = new Service(mockContext);
    mockContext.Verify(c => c.initialize(), Times.Once());
  }
}

但是,由于IContext纯粹是与测试相关的工件,因此我希望避免将其暴露给Service类的用户(通过Service.getContext函数)。这里的解决方案是什么?

1 个答案:

答案 0 :(得分:1)

我的解决方案是将IContext标记为internal并使用as运算符将context投射到Context。这是安全的,因为当用户使用时,实际context始终为Context

internal interface IContext {
    void initialize();
}

public class Context : IContext {
    public void initialize() { }
}

public class ServiceFactory {
    public static Service createService() { return new Service(new Context()); }
}

public class Service {
    public Context getContext() { return context as Context; }

    internal Service(IContext ctx) {
        context = ctx;
        context.initialize();
    }

    private IContext context;
}

[TestFixture()]
public class ServiceTest {

    [Test()]
    public void shouldInitializeContextWhenConstructed() {
        Mock<IContext> mockContext = new Mock<IContext>();
        new Service(mockContext.Object);
        mockContext.Verify(c => c.initialize(), Times.Once());
    }
}