2013-06-11 7 views
23

Sto usando Moq e voglio creare classi di builder per creare i miei mock con predefiniti valori predefiniti preimpostati che possono essere sovrascritti durante l'impostazione della prova, se necessario. L'approccio che ho preso utilizza metodi di estensione in cui passo i valori dei parametri di input e l'output atteso. Nel fare ciò, sto vedendo un comportamento diverso in quello che a me sembra essere un codice semanticamente equivalente: passare It.IsAny() direttamente in una configurazione o passare il valore di It.IsAny() indirettamente in una configurazione. Esempio:Qual è la differenza tra il passaggio di It.IsAny <int>() e il valore di It.IsAny <int>() a un metodo di installazione

public interface IFoo 
{ 
    bool Bar(int value); 
    bool Bar2(int value); 
} 
public class Foo : IFoo 
{ 
    public bool Bar(int value) { return false; } 
    public bool Bar2(int value) { return false; } 
} 

var mock = new Mock<IFoo>(); 
mock.Setup(x => x.Bar(It.IsAny<int>())).Returns(true); 
Assert.IsTrue(mock.Object.Bar(123));     // Succeeds 

var myValue = It.IsAny<int>(); 
mock.Setup(x => x.Bar2(myValue)).Returns(true); 
Assert.IsTrue(mock.Object.Bar2(123));     // Fails 

Le chiamate sono equivalenti (per me), ma la chiamata a Bar2 fallisce asserzione. Perchè è questo?

risposta

33

It.IsAny consente solo al Moq di corrispondere alle chiamate future di chiamate di metodo se utilizzato all'interno del costrutto Setup. Quando il numero Setup viene chiamato Moq, aggiunge semplicemente la chiamata al metodo alla sua cache delle chiamate al metodo già impostate. Si noti che l'argomento per Setup nell'esempio ha tipo Expression<Func<IFoo, bool>>. Dato che si sta passando in uno Expression, la chiamata al metodo effettivo non viene invocata e Moq ha la capacità di attraversare l'espressione per capire quali parametri della chiamata al metodo erano espliciti e quali sono gli argomenti It.IsAny. Utilizza questa capacità per determinare se una chiamata al metodo futuro in fase di runtime corrisponde a una delle chiamate di metodo già impostate.

Per fare in modo che il metodo Bar può accogliere l'argomento It.IsAny<int>(), è necessario fare un ritorno It.IsAny<int>()int (dato che questo è il tipo di parametro di Bar). In generale, il tipo di reso di It.IsAny<T> deve essere T. Deve essere scelto un valore arbitrario di T. La scelta più naturale è default(T), che funziona per tipi di riferimento e tipi di valore. (Ulteriori informazioni sulla parola chiave predefinita here). Nel tuo caso, è default(int), ovvero 0.

Quindi quando si valuta effettivamente It.IsAny<int>() il valore di 0 viene restituito immediatamente. Tuttavia, quando si utilizza It.IsAny<int>() in un Expression (come nell'argomento del metodo Setup), la struttura ad albero della chiamata al metodo viene conservata e Moq può corrispondere alle chiamate del metodo future alla chiamata al metodo incapsulata dallo Expression.

Quindi, anche se non è possibile mantenere It.IsAny<int>() come una variabile in modo significativo, è possibile mantenere l'intero Expression in una variabile:

Expression<Func<IFoo, bool>> myExpr = x => x.Bar2(It.IsAny<int>()); 
mock.Setup(myExpr).Returns(true); 
Assert.IsTrue(mock.Object.Bar2(123)); 

Infine, voglio solo ricordare che Moq è open source. La fonte è disponibile here. Trovo utile avere quel codice sorgente in modo da poter fare clic intorno ed esplorare il codice e i test unitari.

+1

Ottima spiegazione, Ben. Avevo il sospetto che il problema risiedesse nella valutazione dell'espressione, come si descrive, piuttosto che nel valore di ritorno di It.IsAny (). Ho guardato la fonte riflessa in ILSpy e ho potuto vedere It.IsAny , ma non riuscivo a capire come funzionasse riguardo al problema. Grazie ancora per la spiegazione. –

+0

Grazie per aver chiarito quella stregoneria per me! Stavo cercando di dire "var anyFooParam = It.IsAny (); var anyBarParam = It.IsAny ();" per vedere se renderebbe i miei test più leggibili come: .Setup (mock => mock.method (anyFooParam, anyBarParam)). Restituisce (qualcosa!) –

1

It.IsAny<int>() ha tipo di int tornare e restituisce 0, in modo che il secondo configurazione è equivalente a:

mock.Setup(x => x.Bar2(0)).Returns(true); 

non ho controllato il codice moq, ma sono abbastanza sicuro che quando si valuta l'espressione nel metodo di installazione, tiene conto del fatto che il parametro è in realtà It.IsAny rispetto a un numero normale.

Si sta meglio se si creano le impostazioni direttamente nei metodi di supporto e non si passa It.IsAny.

Problemi correlati