Come faccio a Moq un metodo che ha un argomento opzionale nella sua firma senza specificarlo esplicitamente o utilizzando un sovraccarico?


119

Data la seguente interfaccia:

public interface IFoo
{
    bool Foo(string a, bool b = false);
}

Tentativo di deriderlo usando Moq:

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>())).Returns(false);

restituisce il seguente errore in fase di compilazione:

Un albero delle espressioni non può contenere una chiamata o una chiamata che utilizza argomenti facoltativi

Ho trovato il problema di cui sopra sollevato come un miglioramento nell'elenco dei problemi di Moq e sembra essere assegnato alla versione 4.5 (ogni volta che lo è).

La mia domanda è: cosa devo fare dato che quanto sopra non verrà risolto presto? Le mie opzioni sono solo per impostare esplicitamente il valore predefinito del parametro opzionale ogni volta che lo derido (il che sconfigge il punto di specificarne uno in primo luogo) o per creare un sovraccarico senza il bool (come quello che avrei fatto io prima di C # 4)?

O qualcuno ha trovato un modo più intelligente per superare questo problema?


5
Sarebbe ragionevole specificare semplicemente It.IsAny <bool> () per il secondo parametro?
Paul d'Aoust

Un anno e mezzo dopo questo è ancora vero ..
Mukus

@ Mukus, sentiti libero di rilasciare un PR, fratello.
IamDOM

Risposte:


91

Credo che la tua unica scelta in questo momento sia includere esplicitamente il boolparametro nella configurazione di Foo.

Non credo che sconfigga lo scopo di specificare un valore predefinito. Il valore predefinito è una comodità per chiamare il codice, ma penso che dovresti essere esplicito nei tuoi test. Supponi di poter tralasciare di specificare il boolparametro. Cosa succede se, in futuro, qualcuno cambia il valore predefinito di bin true? Ciò porterà a test falliti (e giustamente), ma saranno più difficili da risolvere a causa del presupposto nascosto che bè false. La specifica esplicita del boolparametro ha un altro vantaggio: migliora la leggibilità dei test. Qualcuno che li esamina saprà presto che esiste una Foofunzione che accetta due parametri. Almeno i miei 2 centesimi :)

Quanto a specificarlo ogni volta che lo deridi, non duplicare il codice: crea e / o inizializza il mock in una funzione, in modo da avere un solo punto di cambiamento. Se vuoi davvero, puoi superare l'apparente mancanza di Moq qui duplicando Fooi parametri di in questa funzione di inizializzazione:

public void InitFooFuncOnFooMock(Mock<IFoo> fooMock, string a, bool b = false)
{
    if(!b)
    {
        fooMock.Setup(mock => mock.Foo(a, b)).Returns(false);
    }
    else
    {
        ...
    }
}

1
Ottima risposta; Sono già andato avanti e l'ho specificato esplicitamente nelle mie prese in giro, ma la tua risposta conferma in modo molto chiaro e logico perché dovrei farlo in questo modo. Grazie, @Chris.
Appulus

9
la modifica di un parametro predefinito "dovrebbe" interrompere i test. Il fatto che i test non abbiano esito positivo quando viene modificata un'impostazione predefinita può essere un segno di un cattivo test. Il codice può utilizzare i valori predefiniti ma i test no?
Pop Catalin

È passato un po 'di tempo, ma ho provato questo approccio con Moq quando ho provato a simulare un'interfaccia (IDConnection in Dapper) e continuo a ricevere lo stesso errore. Qualche idea sul perché? Riga di esempio: mockDB.Setup (x => x.Query <MyObject> (It.IsAny <string> (), It.IsAny <DynamicParameters> (), It.IsAny <IDbTransaction> (), false, 600)). Restituisce (new List <MyObject> ()); Gli ultimi due valori sono i parametri opzionali del metodo che sto impostando.
Raelshark

4
Arrgggh! L'orribile if (!x) {} else {}anti-pattern :)
nicodemus13

1
@ nicodemus13 Sì, ma stavo cercando di mantenere l'esempio di codice il più vicino possibile all'esempio dell'OP nella domanda. Non lo sostenere necessariamente :)
Chris Mantle

8

Ho appena riscontrato questo problema oggi, Moq non supporta questo caso d'uso. Quindi, sembra che l'override del metodo sarebbe sufficiente per questo caso.

public interface IFoo
{
    bool Foo(string a);

    bool Foo(string a, bool b);
}

Ora sono disponibili entrambi i metodi e questo esempio funzionerebbe:

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>())).Returns(false);

2

Utilizzando Moq versione 4.10.1 sono stato in grado di fare quanto segue

Con interfaccia:

public interface IFoo
{
    bool Foo(string a, bool b = false);
}

E Mock

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>(), It.IsAny<bool>())).Returns(false);

Risolve una chiamata a Foo con il primo parametro a posto

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.