¿Cómo puedo Moq un método que tiene un argumento opcional en su firma sin especificarlo explícitamente o usar una sobrecarga?

119

Dada la siguiente interfaz:

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

Intentando burlarse de él usando Moq:

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

da el siguiente error en tiempo de compilación:

Un árbol de expresión no puede contener una llamada o invocación que utilice argumentos opcionales.

Encontré el problema mencionado anteriormente como una mejora en la lista de problemas de Moq y parece estar asignado a la versión 4.5 (siempre que sea así).

Mi pregunta es: ¿qué debo hacer dado que lo anterior no se solucionará pronto? ¿Son mis opciones solo establecer explícitamente el valor predeterminado del parámetro opcional cada vez que me burlo de él (lo que anula el punto de especificar uno en primer lugar) o crear una sobrecarga sin el bool (como lo que habría hecho? antes de C # 4)?

¿O alguien ha encontrado una forma más inteligente de superar este problema?

Appulus
fuente
5
¿Sería razonable especificar It.IsAny <bool> () para el segundo parámetro?
Paul d'Aoust
Un año y medio después, esto sigue siendo cierto ..
Mukus
@Mukus, siéntete libre de dejar un PR, hermano.
IamDOM

Respuestas:

91

Creo que su única opción en este momento es incluir explícitamente el boolparámetro en la configuración de Foo.

No creo que anule el propósito de especificar un valor predeterminado. El valor predeterminado es una conveniencia para llamar al código, pero creo que debe ser explícito en sus pruebas. Supongamos que podría omitir la especificación del boolparámetro. ¿Qué sucede si, en el futuro, alguien cambia el valor predeterminado de ba true? Esto conducirá a pruebas fallidas (y con razón), pero serán más difíciles de solucionar debido a la suposición oculta que bes false. La especificación explícita del boolparámetro tiene otro beneficio: mejora la legibilidad de sus pruebas. Alguien que los revise sabrá rápidamente que hay una Foofunción que acepta dos parámetros. Esos son mis 2 centavos, al menos :)

En cuanto a especificarlo cada vez que lo simula, no duplique el código: cree y / o inicialice el simulacro en una función, para que solo tenga un único punto de cambio. Si realmente lo desea, puede superar el aparente defecto de Moq aquí duplicando Foolos parámetros en esta función de inicialización:

public void InitFooFuncOnFooMock(Mock<IFoo> fooMock, string a, bool b = false)
{
    if(!b)
    {
        fooMock.Setup(mock => mock.Foo(a, b)).Returns(false);
    }
    else
    {
        ...
    }
}
Chris Mantle
fuente
1
Excelente respuesta; Ya seguí adelante y lo especifiqué explícitamente en mis simulacros, pero su respuesta confirma de una manera muy clara y lógica por qué debería hacerlo de esta manera. Gracias, @Chris.
Appulus
9
cambiar un parámetro predeterminado "debería" romper las pruebas. El hecho de que las pruebas no fallen cuando se cambia un valor predeterminado puede ser una señal de pruebas incorrectas. ¿El código puede usar los valores predeterminados pero las pruebas no?
Pop Catalin
Ha pasado un tiempo, pero probé este enfoque con Moq al intentar simular una interfaz (IDConnection en Dapper) y sigo recibiendo el mismo error. ¿Alguna idea de por qué? Línea de muestra: mockDB.Setup (x => x.Query <MyObject> (It.IsAny <string> (), It.IsAny <DynamicParameters> (), It.IsAny <IDbTransaction> (), false, 600)). Devuelve (new List <MyObject> ()); Los dos últimos valores son los parámetros opcionales del método que estoy configurando.
Raelshark
4
Arrgggh! El horrible if (!x) {} else {}anti-patrón :)
nicodemus13
1
@ nicodemus13 Sí, pero estaba tratando de mantener el ejemplo de código lo más cerca posible del ejemplo del OP en la pregunta. No necesariamente lo recomendaría :)
Chris Mantle
8

Acabo de encontrar este problema hoy, Moq no admite este caso de uso. Entonces, parece que anular el método sería suficiente para este caso.

public interface IFoo
{
    bool Foo(string a);

    bool Foo(string a, bool b);
}

Ahora ambos métodos están disponibles y este ejemplo funcionaría:

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

Usando Moq versión 4.10.1, he podido hacer lo siguiente

Con interfaz:

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

Y burlarse

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

Resuelve una llamada a Foo con el primer parámetro bien

CF5
fuente