Jak mogę Moq metodę, która ma opcjonalny argument w podpisie bez jawnego określania go lub używania przeciążenia?


119

Biorąc pod uwagę następujący interfejs:

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

Próba kpiny za pomocą Moq:

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

daje następujący błąd w czasie kompilacji:

Drzewo wyrażeń nie może zawierać wywołania ani wywołania używającego opcjonalnych argumentów

Zauważyłem, że powyższy problem został podniesiony jako ulepszenie na liście problemów Moq i wydaje się, że jest on przypisany do wydania 4.5 (jeśli tak jest).

Moje pytanie brzmi: co powinienem zrobić, biorąc pod uwagę, że powyższe nie zostanie naprawione w najbliższym czasie? Czy moje opcje są tylko po to, aby albo jawnie ustawić domyślną wartość opcjonalnego parametru za każdym razem, gdy go mockuję (który rodzaj pokonuje punkt określania jednego w pierwszej kolejności) lub utworzyć przeciążenie bez bool (tak jakbym zrobił przed C # 4)?

Czy może ktoś znalazł bardziej sprytny sposób na rozwiązanie tego problemu?


5
Czy rozsądne byłoby po prostu określenie It.IsAny <bool> () dla drugiego parametru?
— Paul d'Aoust

Półtora roku później jest to nadal prawdą ...
— Mukus

@Mukus, nie krępuj się zostawić PR, brachu.
— IamDOM

Odpowiedzi:


91

Uważam, że teraz jedynym wyborem jest jawne uwzględnienie boolparametru w konfiguracji dla Foo.

Nie sądzę, żeby to przeczyło celowi określenia wartości domyślnej. Wartość domyślna to wygoda przy wywoływaniu kodu, ale myślę, że w testach powinieneś być wyraźny. Powiedzmy, że możesz pominąć określanie boolparametru. Co się stanie, jeśli w przyszłości ktoś zmieni domyślną wartość bna true? Doprowadzi to do niepowodzenia testów (i słusznie), ale będą one trudniejsze do naprawienia z powodu ukrytego założenia, jakim bjest false. Jawne określenie boolparametru ma jeszcze jedną zaletę: poprawia czytelność testów. Ktoś je przeglądając szybko zorientuje się, że istnieje jedna Foofunkcja, która przyjmuje dwa parametry. To przynajmniej moje 2 centy :)

Jeśli chodzi o określanie go za każdym razem, gdy go mockujesz, nie powielaj kodu: utwórz i / lub zainicjuj makietę w funkcji, aby mieć tylko jeden punkt zmiany. Jeśli naprawdę chcesz, możesz pokonać pozorne niedociągnięcia Moqa, powielając Fooparametry w tej funkcji inicjalizacyjnej:

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

1
Doskonała odpowiedź; Już poszedłem do przodu i wyraźnie to określiłem w moich kpinach, ale Twoja odpowiedź w bardzo jasny i logiczny sposób potwierdza, dlaczego powinienem to zrobić w ten sposób. Dzięki, @Chris.
— Appulus

9
zmiana domyślnego parametru „powinno” przerywać testy. Brak niepowodzenia testów po zmianie wartości domyślnej może być oznaką złego testowania. Kod może używać wartości domyślnych, ale testy nie?
— Pop Catalin

Minęło trochę czasu, ale wypróbowałem to podejście z Moq, próbując mockować interfejs (IDConnection w Dapper) i nadal otrzymuję ten sam błąd. Jakieś pomysły, dlaczego? Przykładowy wiersz: mockDB.Setup (x => x.Query <MyObject> (It.IsAny <string> (), It.IsAny <DynamicParameters> (), It.IsAny <IDbTransaction> (), false, 600)). Zwraca (new List <MyObject> ()); Ostatnie dwie wartości to opcjonalne parametry konfigurowanej przeze mnie metody.
— Raelshark

4
Arrgggh! Straszny if (!x) {} else {}anty-wzór :)
— nicodemus13

1
@ nicodemus13 Tak, ale starałem się, aby przykład kodu był jak najbardziej zbliżony do przykładu OP w pytaniu. Niekoniecznie bym tego popierał :)
— Chris Mantle

8

Właśnie dzisiaj napotkałem ten problem, Moq nie obsługuje tego przypadku użycia. Wydaje się więc, że zastąpienie metody byłoby wystarczające w tym przypadku.

public interface IFoo
{
    bool Foo(string a);

    bool Foo(string a, bool b);
}

Teraz obie metody są dostępne i ten przykład zadziała:

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

2

Korzystając z Moq w wersji 4.10.1 mogłem wykonać następujące czynności

Z interfejsem:

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

I Mock

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

Rozwiązuje wywołanie Foo z pierwszym parametrem w porządku

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.