2009-04-17 1 views
2

Ein anderer Tag, eine andere Frage. Meine Dienstschicht hat die folgende MethodeMoq - Wie testen Tests Änderungen an einer Referenz in einer Methode

public MatchViewData CreateMatch(string user) 
{ 
    var matchViewData = !HasReachedMaxNumberOfMatchesLimit(user) ? 
     CreateMatchAndAddToRepository(user) : 
     MatchViewData.NewInstance(new Match(user)); 

    matchViewData.LimitReached = HasReachedMaxNumberOfMatchesLimit(user); 
    return matchViewData; 
} 

Verfahren die diese Hilfsmethode ruft ein neues Match-Objekt zu erstellen:

private MatchViewData CreateMatchAndAddToRepository(string user) 
{ 
    var match = new Match(user); 
    MatchRepository.Add(match); 
    return MatchViewData.NewInstance(match); 
} 

Die Repository speichert die angegebene Match-Objekts und stellt die ID auf einen Wert> 0.

public void Add(Match match) 
{ 
    Check.Require(match != null); 
    var numberOfMatchesBefore = Matches.Count; 
    SetIdPerReflection(match, NextVal()); 
    Matches.Add(match); 
    Check.Ensure(numberOfMatchesBefore == Matches.Count - 1); 
} 

Das matchviewdata-Objekt kopiert einige Eigenschaften des Match-Objekts (einschließlich der ID)

Mein Komponententest sollte sicherstellen, dass das resultierende viewdata-Objekt im Service eine ID> 0 hat. Um dies zu erreichen, muss ich das Repository und das Verhalten der add-Methode vortäuschen. Die Service-Methode erstellt jedoch jedes Mal ein neues Match-Objekt, und die add-Methode im Repository aktualisiert das referenzierte Match-Objekt (es ist kein Rückgabewert erforderlich). Ich habe keine Idee, das mit moq zu lösen.

Dies ist meine Unit-Test so weit:

[Test] 
public void ServiceCreateMatchReturnedMatchViewDataHasNonZeroId() 
{ 
    var match = TestUtils.FakePersistentMatch(User, 1); 
    var repositoryMock = new Mock<IMatchRepository>(); 
    repositoryMock.Setup(
      r => r.Add(It.IsAny<Match>())).Callback(() => match.Id = 1); 
    var serviceFacade = new DefaultServiceFacade(repositoryMock.Object); 

    var returnedMatch = serviceFacade.CreateMatch(User); 

    Assert.That(returnedMatch.Id, Is.GreaterThan(0)); 
} 

ich einige andere Varianten ausprobiert - nichts funktioniert.

Antwort

3

Es sieht für mich Ihr Problem ist in dieser Zeile;

repositoryMock.Setup(
     r => r.Add(It.IsAny<Match>())).Callback(() => match.Id = 1); 

Was du hier eigentlich die ID des ersten Treffers Objekt setzt Sie in Ihrem Test erklärt haben, nicht das neue Spiel in Ihrem Dienst erstellt.

Da das Objekt Match, das Sie dem Repository zur Verfügung stellen, intern erstellt wird, kann ich mir keinen einfachen Weg vorstellen, es in Ihrer Testmethode zu referenzieren, um einen Rückruf dafür einzurichten. Für mich ist dies ein Zeichen, dass Sie versuchen, zu viel in einem Einheitstest zu testen.

Ich denke, Sie sollten einfach testen, dass die Add-Methode aufgerufen wird und einen separaten Test schreiben, um sicherzustellen, dass es wie erwartet funktioniert.

Ich schlage so etwas vor;

[Test] 
public void ServiceAddsNewMatchToRepository() 
{ 
    var repositoryMock = new Mock<IMatchRepository>(); 
    bool addCalled = false; 
    repositoryMock 
     .Expect(r => r.Add(It.Is<Match>(x => x.Id == 0)) 
     .Callback(() => addCalled = true); 

    var serviceFacade = new DefaultServiceFacade(repositoryMock.Object); 
    serviceFacade.CreateMatch(User); 

    Assert.True(addCalled); 
} 

.... 

[Test] 
public void AddingANewMatchGeneratesANewId() 
{ 
    var match = new Match(user); 
    var matchRepository = new MatchRepository(); 
    var returnedMatch = matchRepository.Add(match); 

    Assert.That(returnedMatch.Id, Is.GreaterThan(0));  
} 
+0

Oh Schatz. Natürlich haben Sie Recht :-) (und ich hatte bereits den zweiten Test). Aber manchmal sieht man den Wald nicht für die Bäume ... Danke –

+0

Statt '.Callback (() => match.Id = 1)' wo das an Moq übergebene Argument ** nicht ** im Aufruf verwendet wird -back, und die 'match' modifizierte ist eine lokale Variable (was nicht hilfreich ist), @HerrW. kann einfach eine andere Überladung von 'Callback' verwenden, die das tatsächliche Argument verwendet. Es ist so einfach wie: '.Callback ((Match m) => m.Id = 1)'. Für komplexere Sachen kann natürlich ein Lambda mit einem Körper verwendet werden: '.Callback ((Match m) => {m.Id = 1;/* weitere Anweisungen können hier gehen * /})'. –

Verwandte Themen