2016-07-05 14 views
1

Bitte werfen Sie einen Blick auf die folgende Probe mit:NSubstitute, behaupten bei eingehenden Anrufen wird Argumente gegen object.ReferenceEquals

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(); 
     } 
    } 

Und den folgenden Test:

[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 
    } 

Das Problem ist, dass ich Ich weiß nicht, wie man die Argumente im empfangenen Call (CallMethod) testet. Wie es ist, werden die Argumente mit ersten object.ReferenceEquals und dann object.Equals verglichen, und da ich in der Regel keine Kontrolle über die gelieferten Daten an die Methode habe, verweisen die Objekte (data und expectedResult) niemals auf dasselbe Objekt.

Allerdings gibt es eine Möglichkeit, es funktioniert, und das ist, wenn ich Equals außer Kraft setzen, wie folgt aus:

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

Dies funktioniert, aber ich will nicht Equals implementieren, einen Test zu erfüllen weil es alle möglichen anderen Auswirkungen haben wird, die hier nicht erwähnenswert sind.

Was ich will, ist ein Vergleich die gleiche eine Linie, die die zweite assert tun:

data.ShouldBeEquivalentTo(expectedResult); 

Aber dies ist nicht standardmäßig unterstützt.

Also, wie löse ich dieses Problem. Danke.

Antwort

1

NSubstitute haben nicht vollständig gebacken Unterstützung für diese im Moment (v1.10). Issue 160 hat eine Diskussion darüber.

Eine Option, wie David Osborne erwähnte, ist es, die Argumente zu erfassen und sie mit Ihrer Assertion-Bibliothek der Wahl zu bestätigen.

Eine weitere Möglichkeit besteht darin, einen benutzerdefinierten Argument-Matcher zu verwenden. Ich habe ein Beispiel aus this comment enthalten:

[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) { ... } 

Wie im Kommentar beschrieben, das funktioniert hat aber Probleme mit einer ziemlich schrecklich Fehlermeldung bereitstellt, wenn es fehlschlägt.

Es gibt auch ein Beispiel für hooking NSubstitute up to FluentAssertions.

+0

Nun, ich stimme zu, die Fehlermeldung ist nicht schön, aber es ist ein Workaround und wird für den Moment tun, bis eine bessere Implementierung veröffentlicht wird ... –

0

Sie können speichern, was passiert ist und dann später vergleichen:

[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); 
} 
+0

Das stimmt, aber dann werde ich immer noch nicht wissen, ob CalledMethod einen Anruf mit den genauen Argumenten erhalten hat oder nicht, richtig ...! –

+0

Im Kontext des Tests tun Sie, weil 'methodReceivedThis' nur durch einen Aufruf von' CalledMethod() 'gesetzt werden kann. –

+0

Wenn ich das Ergebnis schon wüsste, müsste ich es nicht testen, richtig :-) Offensichtlich würde ich in der realen Welt nicht wissen, was für eine Methode - die eine andere Methode ruft, die vielleicht noch eine andere Methode nennt - würde zu dieser Methode liefern und deshalb teste ich, für welche Argumente ich erwarten würde, dass es erhält. –