NSubstitute,在收到调用时断言,使用object.ReferenceEquals比较参数

时间:2016-07-05 10:32:11

标签: .net unit-testing nsubstitute

请查看以下示例:

    public interface IDomainClass
    {
        int A { get; set; }
        void CalledMethod(IDomainClass data);
    }
    public class DomainClass : IDomainClass
    {
        public int A { get; set; }

        public void CalledMethod(IDomainClass data)
        {
            throw new NotImplementedException();
        }
    }

以下测试:

    [Test]
    public void TestSample()
    {
        //Arrange
        IDomainClass testingClass = Substitute.For<IDomainClass>();

        IDomainClass data = new DomainClass() { A = 123, };
        IDomainClass expectedResult = new DomainClass() { A = 123, };

        //Act
        testingClass.CalledMethod(data);

        //Assert
        testingClass.ReceivedWithAnyArgs(1).CalledMethod(null); //ok
        data.ShouldBeEquivalentTo(expectedResult);              //ok
        testingClass.Received(1).CalledMethod(expectedResult);  //fail
    }

问题在于我不知道如何测试接收到的调用中的参数(CallMethod)。实际上,使用第一个object.ReferenceEquals和object.Equals来比较参数,并且因为我通常无法控制传递给方法的数据,所以对象(data和expectedResult)从不引用相同的对象

然而,有一种方法可以使它工作,那就是我重写Equals,就像这样:

        public override bool Equals(object obj)
        {
            return this.A.Equals((obj as DomainClass).A);
        }
        public override int GetHashCode()
        {
            return this.A.GetHashCode();
        }

这样可行,但我不想实现Equals以满足测试要求,因为它会带来所有其他不值得提及的含义。

我想要的是比较器在第二个断言行上做同样的事情:

data.ShouldBeEquivalentTo(expectedResult);

但默认情况下不支持此功能。

那么,我该如何解决这个问题呢。 谢谢。

3 个答案:

答案 0 :(得分:1)

您可以存储传递的内容,然后再进行比较:

[Test]
public void TestSample()
{
    //Arrange
    IDomainClass testingClass = Substitute.For<IDomainClass>();

    IDomainClass data = new DomainClass() { A = 123, };

    IDomainClass methodReceievedThis = null;

    testingClass
        .When(t => t.CalledMethod(Arg.Any<IDomainClass>())
        .Do(p => methodReceievedThis = p);

    //Act
    testingClass.CalledMethod(data);

    //Assert
    testingClass.ReceivedWithAnyArgs(1).CalledMethod(null); //ok

    methodReceievedThis.ShouldBeEquivalentTo(data);
}

答案 1 :(得分:1)

NSubstitute目前还没有完全支持这一点(v1.10)。 Issue 160对此进行了一些讨论。

David Osborne提到的一个选项是捕获参数并使用您选择的断言库断言它们。

另一种方法是使用自定义参数匹配器。我已经在this comment中添加了一个示例:

[Test]
public void UsingArgMatcher() {
    var repos = Substitute.For<IRepos>();

    var sut = new SomeService(repos);
    sut.Process();

    repos.Received().Save(Arg.Is(EquivalentTo(new int[] { 1, 2, 3 })));
}

private Expression<Predicate<IEnumerable<T>>> EquivalentTo<T>(IEnumerable<T> enumerable) {
    return x => Equiv(enumerable, x);
}

private bool Equiv<T>(IEnumerable<T> a, IEnumerable<T> b) { ... }

如评论中所述,这可行,但在失败时提供非常可怕的错误消息时会出现问题。

还有一个hooking NSubstitute up to FluentAssertions的例子。

答案 2 :(得分:1)

我遇到了同样的问题,这为我解决了这个问题:

[Test]
public void TestSample()
{
    //Arrange
    IDomainClass testingClass = Substitute.For<IDomainClass>();

    IDomainClass data = new DomainClass() { A = 123, };

    //Act
    testingClass.CalledMethod(data);

    //Assert
    testingClass.Received(1).CalledMethod(Arg.Is<DomainClass>(x => x.A == 123));  //this worked for me
}