Pracuję na .NET 4.0 używając C # w Windows 7.
Chcę przetestować komunikację między niektórymi metodami za pomocą makiety. Jedynym problemem jest to, że chcę to zrobić bez implementacji interfejsu. Czy to jest możliwe?
Po prostu przeczytałem wiele tematów i kilka samouczków na temat pozorowanych obiektów, ale wszystkie z nich były używane do mockowania interfejsów, a nie klas. Próbowałem użyć frameworków Rhino i Moq.
Odpowiedzi:
Po prostu oznacz dowolną metodę, którą chcesz udawać, jako
virtual
(a nie prywatną). Wtedy będziesz mógł stworzyć fałszywkę, która może zastąpić metodę.Jeśli używasz
new Mock<Type>
i nie masz konstruktora bez parametrów, możesz przekazać parametry jako argumenty powyższego wywołania, ponieważ przyjmuje ono typparam Objects
źródło
Większość mockujących frameworków (w tym Moq i RhinoMocks) generuje klasy proxy jako substytut dla twojej mockowanej klasy i zastępuje metody wirtualne zachowaniem, które zdefiniujesz. Z tego powodu można tylko pozorować interfejsy lub metody wirtualne w klasach konkretnych lub abstrakcyjnych. Ponadto, jeśli mockujesz konkretną klasę, prawie zawsze musisz podać konstruktor bez parametrów, aby modelowa struktura wiedziała, jak utworzyć instancję klasy.
Skąd niechęć do tworzenia interfejsów w swoim kodzie?
źródło
Dzięki MoQ możesz kpić z konkretnych zajęć:
var mocked = new Mock<MyConcreteClass>();
ale to pozwala na przesłonięcie
virtual
kodu (metody i właściwości).źródło
new Mock<MyConcreteClass>(param1, anotherParam, thirdParam, evenMoreParams);
Myślę, że lepiej jest stworzyć interfejs dla tej klasy. I utwórz test jednostkowy za pomocą interfejsu.
Jeśli nie masz dostępu do tej klasy, możesz utworzyć adapter dla tej klasy.
Na przykład:
public class RealClass { int DoSomething(string input) { // real implementation here } } public interface IRealClassAdapter { int DoSomething(string input); } public class RealClassAdapter : IRealClassAdapter { readonly RealClass _realClass; public RealClassAdapter() => _realClass = new RealClass(); int DoSomething(string input) => _realClass.DoSomething(input); }
W ten sposób możesz łatwo utworzyć makietę dla swojej klasy za pomocą IRealClassAdapter.
Mam nadzieję, że to działa.
źródło
Standardowe struktury do mockowania tworzą klasy proxy. To jest powód, dla którego są technicznie ograniczone do interfejsów i metod wirtualnych.
Jeśli chcesz również naśladować „normalne” metody, potrzebujesz narzędzia, które współpracuje z instrumentacją zamiast generowania proxy. Np. MS Moles i Typemock mogą to zrobić. Ale ten pierwszy ma okropne „API”, a drugi jest komercyjny.
źródło
Jeśli nie możesz zmienić testowanej klasy, jedyną opcją, którą mogę zasugerować, jest użycie MS Fakes https://msdn.microsoft.com/en-us/library/hh549175.aspx . Jednak MS Fakes działa tylko w kilku wersjach programu Visual Studio.
źródło
Jeśli jest gorzej, możesz utworzyć interfejs i parę adapterów. Zmieniłbyś wszystkie zastosowania ConcreteClass, aby zamiast tego używał interfejsu i zawsze przekazywał adapter zamiast konkretnej klasy w kodzie produkcyjnym.
Adapter implementuje interfejs, więc makieta może również implementować interfejs.
To bardziej tworzenie szkieletów niż tylko tworzenie wirtualnej metody lub po prostu dodawanie interfejsu, ale jeśli nie masz dostępu do źródła dla konkretnej klasy, może to wydostać cię z wiązania.
źródło
Spotkałem się z czymś takim w jednym ze starych i starszych projektów, w którym pracowałem, który nie zawiera żadnych interfejsów ani najlepszych praktyk, a także jest zbyt trudne, aby wymusić na nich ponowne tworzenie rzeczy lub refaktoryzację kodu ze względu na dojrzałość biznesową projektu, więc w moim projekcie UnitTest tworzyłem Wrapper na klasach, które chcę mockować, i interfejs implementacji opakowania, który zawiera wszystkie moje potrzebne metody, które chcę skonfigurować i pracować z nimi. Teraz mogę wyszydzać opakowanie zamiast prawdziwej klasy.
Na przykład:
Usługa, którą chcesz przetestować, która nie zawiera metod wirtualnych ani interfejsu implementacji
public class ServiceA{ public void A(){} public String B(){} }
Wrapper do moq
public class ServiceAWrapper : IServiceAWrapper{ public void A(){} public String B(){} }
Interfejs Wrapper
public interface IServiceAWrapper{ void A(); String B(); }
W teście jednostkowym możesz teraz mockować opakowanie:
public void A_Run_ChangeStateOfX() { var moq = new Mock<IServiceAWrapper>(); moq.Setup(...); }
Może to nie jest najlepsza praktyka, ale jeśli reguły Twojego projektu narzucają Ci to w ten sposób, zrób to. Ponadto umieść wszystkie opakowania w projekcie testu jednostkowego lub projekcie pomocnika określonym tylko dla testów jednostkowych, aby nie przeciążać projektu niepotrzebnymi opakowaniami lub adapterami.
Aktualizacja: Ta odpowiedź od ponad roku, ale w tym roku spotkałem się z wieloma podobnymi scenariuszami z różnymi rozwiązaniami. Na przykład tak łatwo jest używać Microsoft Fake Framework do tworzenia mocków, fake i stubów, a nawet testowania prywatnych i chronionych metod bez żadnych interfejsów. Możesz przeczytać: https://docs.microsoft.com/en-us/visualstudio/test/isolating-code-under-test-with-microsoft-fakes?view=vs-2017
źródło