2013-07-29 9 views
5

Utilizzando FakeItEasy, come faccio a verificare se il metodo del mio oggetto chiama un altro metodo su questo stesso oggetto?Usa A.CallTo() di FakeItEasy su un altro metodo nello stesso oggetto

Il Test:

[TestMethod] 
public void EatBanana_CallsWillEat() 
{ 
    var banana = new Banana(); 
    var myMonkey = new Monkey(); 

    myMonkey.EatBanana(banana); 

    //this throws an ArgumentException, because myMonkey is a real instance, not a fake 
    A.CallTo(() => myMonkey.WillEat(banana) 
    .MustHaveHappened(); 
} 

La Classe:

public class MyMonkey { 
    private readonly IMonkeyRepo _monkeyRepo; 

    public MyMonkey(IMonkeyRepo monkeyRepo) { 
    _monkeyRepo = monkeyRepo; 
    } 

    public void EatBanana(Banana banana) { 
    //make sure the monkey will eat the banana 
    if (!this.WillEat(banana)) { 
     return; 
    } 

    //do things here 
    } 

    public bool WillEat(Banana banana) { 
    return !banana.IsRotten; 
    } 
} 

io sono aperto a suggerimenti. Se sto andando in questo modo tutto sbagliato, per favore fatemelo sapere.

+2

Questo non è proprio ciò per cui FIE è, come ho capito. FIE fornisce oggetti falsi in modo che il codice di produzione possa essere più facilmente infilato e spinto. Come fai notare, non hai un falso. Nella mia esperienza, questo tipo di test di solito non è una buona idea. La tua classe MyMonkey dovrebbe essere un'unità abbastanza autonoma, e sarebbe meglio testare il suo comportamento generale quando viene ordinato di mangiare una banana, piuttosto che preoccuparsi se ha chiamato i suoi metodi. Ad esempio, saresti in grado di dire se la banana è stata mangiata sulla base di indizi di ciò che accade in "// fare le cose qui"? –

+0

@BlairConrad nel mio scenario reale, WillEat è più complesso e ha i suoi test, e questo è solo uno dei test di EatBanana. In questo test, se lascio che EatBanana chiami il vero WillEat, non testerò due funzionalità in un test? Quindi, se WillEat fosse cambiato, potrebbe interrompere quel test, che è una cattiva notizia, giusto? –

+4

Vedo il tuo punto. È difficile. Se 'WillEat' vivesse su un altro oggetto, saremmo tutti sollecitati a simulare quell'oggetto e a iniettarlo in' MyMonkey' (sembra doloroso). Tutto quello che posso dire è che penso che la migliore posizione di default sarebbe provare a testare "MyMonkey" dall'esterno, facendo affidamento sui risultati osservabili di un cliente. Ma, ci hai pensato sopra e hai trovato una soluzione che ti farà risparmiare dolore e riduce il numero di test e il numero che si interromperà per un dato. Conosci il codice migliore, quindi se funziona per te ... Volevo solo che tu sapessi dell'alternativa. –

risposta

2

È possibile farlo. Se il metodo WillEat era virtuale, altrimenti FakeItEasy non sarà in grado di simularlo.

Con questo cambiamento, si potrebbe fare questo:

[TestMethod] 
public void EatBanana_CallsWillEat() 
{ 
    var fakeMonkey = A.Fake<MyMonkey>(); 

    fakeMonkey.EatBanana(new Banana()); 

    A.CallTo(()=>fakeMonkey.WillEat(A<Banana>._)).MustHaveHappened(); 
} 

Io non sono ancora convinto che sia una buona idea (come ho sbraitava nei commenti) - Penso che sarebbe meglio fare affidamento su altri comportamento osservabile, ma non ho familiarità con il tuo sistema. Se pensi che questo sia il modo migliore, il codice di esempio dovrebbe funzionare per te.

3

Perché stai prendendo in giro oggetti testati? Cosa esattamente stai provando a testare? La verifica della chiamata a WillEat è di scarso valore. Quali informazioni serve al consumatore? Dopo tutto, al consumatore non importa come viene implementato il metodo. I consumatori si preoccupano di ciò che sono i risultati.

Cosa succede quando la scimmia mangia banana che non è marcio? Il test dovrebbe rispondere a questa domanda:

[TestMethod] 
public void EatBanana_CAUSES_WHAT_WhenBananaIsNotRotten() 
{ 
    var repo = A.Fake<IMonkeyRepo>(); 
    var monkey = new Monkey(repo); 
    var freshBanana = new Banana { IsRotten = false }; 

    monkey.EatBanana(freshBanana); 

    // verifications here depend on what you expect from 
    // monkey eating fresh banana 
} 

Nota che si può fare tutti i tipi di verifiche a IMonkeyRepo, che è propriamente falso e iniettata qui.

+0

Buon punto. Se non sto prendendo in giro WillEat e consento a EatBanana di chiamare il vero WillEat, sto testando due funzionalità in un solo test?In questo esempio, WillEat è banale, ma nella mia situazione attuale, è più complesso e restituisce un oggetto derivato basato sui suoi argomenti, quindi ho pensato di deriderlo e verificare che fosse chiamato sarebbe la strada giusta da percorrere. Al momento ho test separati per WillEat e un paio di altri test per EatBanana, inclusa la verifica che il repository sia stato chiamato. Forse sto diventando troppo granulare? –

+3

@JoshNoe: o forse non stai diventando abbastanza granulare. Se 'WillEat' è così complesso, forse dovrebbe vivere nella sua stessa classe? Forse ha più senso estrapolare questa funzionalità a * food-valutator-sort-of-thing * e iniettarla a monkey? I test * dovrebbero * essere semplici - se non lo sono * di solito * è un buon indicatore del fatto che il design potrebbe essere rivisitato. Nota a margine, perché "Monkey" dipende da "IMonkeyRepository"? –

Problemi correlati