Moq和访问被调用的参数

时间:2010-01-14 11:32:35

标签: unit-testing design-patterns moq oop xunit.net

我刚开始在已经建立的项目中实施单元测试(使用xUnit和Moq)。该项目通过统一容器广泛使用依赖注入。

我有两个服务A和B.服务A是在这种情况下正在测试的服务。服务A调用B并为其提供内部函数的委托。这个'回调'用于在收到必须处理的消息时通知A.

因此A调用(其中b是服务B的实例):

b.RegisterHandler(Guid id, Action<byte[]> messageHandler);

为了测试服务A,我需要能够调用messageHandler,因为这是它当前接受消息的唯一方式。

可以使用Moq完成吗?即。我可以模拟服务B,这样当调用RegisterHandler时,messageHandler的值会传递给我的测试吗?

或者我需要重新设计吗?在这种情况下,我应该使用任何设计模式吗?有谁知道这种设计有什么好的资源吗?

2 个答案:

答案 0 :(得分:9)

您可以使用Mock上的Callback(名称相似性是偶然的)方法获取回调实例(或任何其他输入参数):

[TestMethod]
public void Test19()
{
    Action<byte[]> callback = null;

    var bSpy = new Mock<IServiceB>();
    bSpy.Setup(b => b.RegisterHandler(It.IsAny<Guid>(), It.IsAny<Action<byte[]>>()))
        .Callback((Guid g, Action<byte[]> a) => callback = a);

    var sut = new ServiceA(bSpy.Object);
    sut.RegisterCallback();

    Assert.AreEqual(sut.Do, callback);
}

当ServiceA定义为:

时,此方法有效
public class ServiceA
{
    private readonly IServiceB b;

    public ServiceA(IServiceB b)
    {
        if (b == null)
        {
            throw new ArgumentNullException("b");
        }

        this.b = b;
    }

    public void RegisterCallback()
    {
        this.b.RegisterHandler(Guid.NewGuid(), this.Do);
    }

    public void Do(byte[] bytes)
    {
    }
}

答案 1 :(得分:0)

是的,您可以设置Moq对象以响应预期和意外操作。 这是一个Visual Studio单元测试示例......

[TestMethod]
public void MyTest()
    {
      var moqObject = new Mock<ServiceB>();

      // Setup the mock object to throw an exception if a certain value is passed to it...
      moqObject.Setup(b => b.RegisterHandle(unexpectedValue).Throws(new ArgumentException());

      // Or, setup the mock object to expect a certain method call...
      moqObject.Setup(b => b.RegisterHandle(expectedValue));

      var serviceA = new ServiceA(moqObject.Object);

      serviceA.DoSomethingToTest();

      // This will throw an exception if an expected operation didn't happen...
      moqObject.VerifyAll();
    }