2017-05-09 2 views
0

Ich möchte eine Methode testen, die eine Bibliothek eines Drittanbieters abfragt. Die Bibliothek gibt ein Objekt mit der Eigenschaft IReadOnlyCollection zurück.Mock-Methode Rückgabewert mit schreibgeschütztem Merkmal

Es gibt keinen Konstruktor, um den Wert der Eigenschaft festzulegen, und das Objekt hat keine Schnittstelle für mich, um zu spotten.

Ich habe Moq verwendet, um die Schnittstelle für den Dienst, den ich aufrufen, zu verspotten, aber ich kann keinen verspotteten Rückgabewert erstellen, da ich die Eigenschaft nicht festlegen kann.

public interface IHitService { 
    public Hit GetHit(); 
} 

public class Hit { 
    public Hit() { 
    } 

    public IReadOnlyCollection<string> Result { get; } 
} 

public class TestingClass { 
    public void MyTest() { 
     Hit hit = new Hit() { 
      // cannot set this property 
      Result = new List<string>() { "hello","goodbye" }; 
     } 

     Mock<IHitService> service = new Mock<IHitService>(); 
     service.Setup(c => c.GetHit).Returns(hit); 
    } 
} 

Was wäre der beste Weg für mich, den Rückgabewert zu generieren, um meine Methode zu testen? Das Wrapping des Objekts mit einer new-Eigenschaft zum Verbergen der Basis funktioniert nicht.

+0

Wie wird es normalerweise eingestellt? –

+0

und spielt es eine Rolle, wenn es nicht gesetzt ist? es ist eine Third Party Sache. Normalerweise testen Sie das nicht, nur Ihren eigenen Code –

+0

Wir manipulieren die Daten in der Methode, die ich gerade teste. Es ruft 'GetHit()' auf und arbeitet mit den Daten in der 'Result'-Sammlung. Die Eigenschaft war ursprünglich ein IEnumerable, aber die neueste Version hat es in eine ReadOnlyCollection geändert. Ich bin mir nicht sicher, wie es in der Bibliothek steht. Das ist eine gute Idee, ich werde den Quellcode überprüfen. – hsimah

Antwort

2

Sie Unit-Test-Frameworks verwenden, die es Ihnen ermöglichen, das Verhalten zu ändern B. in diesem Fall habe ich Typemock Isolator verwendet, um Ihr Problem zu lösen. Sie können den Rückgabewert der Ergebniseigenschaft ändern, sodass Sie Ihren Test "einstellen" können, ohne Ihren Code zu ändern oder zusätzlichen Code hinzuzufügen :

public void TestMethod1() 
{ 
    List<string> s = new List<string> { "sfas", "asfsa", "blbba" }; 
    var hit = Isolate.Fake.NextInstance<Hit>(); 

    Isolate.WhenCalled(() => hit.Result).WillReturnCollectionValuesOf(s); 
} 

In diesem Test verspottete ich die Hit - Klasse und den Rückgabewert der Result -Eigenschaft in eine Liste von Strings geändert, die ich erstellt habe.

1

Wenn Sie eine Drittanbieter-Bibliothek testen müssen, wäre es eine bessere Idee, Ihre eigene Abstraktion (Schnittstelle) zu schaffen und sowohl darauf verläßt für die Prüfung und echten Code:

public interface IHitService 
{ 
    IHit GetHit(); 
} 

public interface IHit 
{ 
    IReadOnlyCollection<string> Result { get; } 
} 

bei Ihrer Bewerbung können Sie eine einfache Wrapperklasse erstellen, die IHit implementiert, indem Sie sie an die konkrete Drittanbieterklasse delegieren. Jetzt können Sie die Oberfläche testen, indem Sie sie nach Bedarf verspotten.

+0

Das sieht wie meine Option aus. Es ist keine einfache Klasse, aber die Arbeit muss getan werden. Vielen Dank für Ihre Zeit. – hsimah

+1

Sicher, glücklich zu helfen. Dies kann auch ein Testgeruch sein, den Sie zu niedrig testen. In vielen Fällen kann es sinnvoller sein, _ihre_ Service-Interfaces zu testen, die möglicherweise die Bibliothek verbrauchen, anstatt die Bibliotheksaufrufe selbst zu testen/zu verspotten. –

+0

Ja, das ist eine knifflige Situation, in der, abhängig vom 'Ergebnis', verschiedene Dinge passieren, also muss ich das Eingabeobjekt steuern - und dann ändert der Entwickler die Zugriffsebene auf mich! – hsimah

1

Im Allgemeinen, wenn Sie nicht 3rd-Party-Code ändern können, einen Adapter bauen und nutzen Sie Ihre eigene Abstraktion: -

public interface IHit 
{ 
    IReadOnlyCollection<string> Result { get; } 
} 

public interface IHitService 
{ 
    IHit GetHit(); 
} 

public class HitAdapter : IHit 
{ 
    private Hit _hit; 

    public HitAdapter(Hit hit) 
    { 
     _hit = hit; 
    } 

    public IReadOnlyCollection<string> Result => _hit.Result; 
} 


public class TestingClass 
{ 
    public void MyTest() 
    { 
     var hitMock = new Mock<IHit>(); 
     hitMock.Setup(c => c.Result).Returns<IReadOnlyCollection<string>>(x => new List<string>() {"hello", "goodbye"}); 
     var service = new Mock<IHitService>(); 
     service.Setup(c => c.GetHit()).Returns<IHit>(x => hitMock.Object); 
    } 
} 
+0

Das sieht wie meine Option aus. Es ist keine einfache Klasse, aber die Arbeit muss getan werden. Vielen Dank für Ihre Zeit. – hsimah

+1

Wenn Sie Resharper haben, braucht es viel von der Eselsarbeit beim Generieren des delegierenden Adapters –

Verwandte Themen