Chciałbym wstrzyknąć próbny obiekt Mockito do fasoli Spring (3+) na potrzeby testów jednostkowych z JUnit. Moje zależności fasoli są obecnie wstrzykiwane za pomocą @Autowired
adnotacji na prywatnych polach członkowskich.
Zastanawiałem się nad użyciem, ReflectionTestUtils.setField
ale instancja komponentu bean, którą chcę wstrzyknąć, jest w rzeczywistości proxy i dlatego nie deklaruje pól prywatnych członków klasy docelowej. Nie chcę tworzyć publicznych ustawień dla zależności, ponieważ będę modyfikował mój interfejs wyłącznie do celów testowych.
Postępowałem zgodnie z radami społeczności Spring, ale próbka nie została utworzona, a automatyczne okablowanie nie powiodło się:
<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.package.Dao" />
</bean>
Występujący obecnie błąd jest następujący:
...
Caused by: org...NoSuchBeanDefinitionException:
No matching bean of type [com.package.Dao] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this dependency.
Dependency annotations: {
@org...Autowired(required=true),
@org...Qualifier(value=dao)
}
at org...DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(D...y.java:901)
at org...DefaultListableBeanFactory.doResolveDependency(D...y.java:770)
Jeśli ustawię constructor-arg
wartość na coś nieprawidłowego, nie wystąpi błąd podczas uruchamiania kontekstu aplikacji.
Odpowiedzi:
Najlepszym sposobem jest:
Aktualizacja
W pliku kontekstowym próbka musi być wymieniona przed zadeklarowaniem dowolnego pola automatycznego w zależności od niego.
źródło
Spowoduje to wstrzyknięcie wyśmiewanych obiektów do klasy testowej. W takim przypadku wstrzyknie mockedObject do testObject. Zostało to wspomniane powyżej, ale tutaj jest kod.
źródło
mockedObject
?Mockito.spy(...)
na tomockedObject
zamiast? A następnie użyjwhen(mockedObject.execute).thenReturn(objToReturn)
lubdoReturn(objToReturn).when(mockedObject).execute()
. Drugi nie wywołuje prawdziwej metody. Możesz również sprawdzićMockito.doCallRealMethod()
dokumentacjęMam bardzo proste rozwiązanie przy użyciu Spring Java Config i Mockito:
źródło
initMocks
? Dlaczego nie tylkoreturn Mockito.mock(BeanA.class)
w środkugetBeanA
? W ten sposób jest to prostsze i jest mniej kodu. czego mi brakuje?Dany:
Możesz mieć klasę, która jest testowana, ładowaną przez automatyczne okablowanie, wyśmiewać zależność z Mockito, a następnie użyć Spring ReflectionTestUtils, aby wstrzyknąć próbkę do testowanej klasy.
Należy pamiętać, że przed wiosną 4.3.1 ta metoda nie będzie działać z usługami za proxy (z adnotacjami
@Transactional
lubCacheable
, na przykład). Zostało to naprawione przez SPR-14050 .W przypadku wcześniejszych wersji rozwiązaniem jest rozpakowanie serwera proxy, jak tam opisano: Adnotacja transakcyjna pozwala uniknąć wyśmiewania usług (co
ReflectionTestUtils.setField
teraz robi domyślnie)źródło
Jeśli używasz Spring Boot 1.4, jest to świetny sposób na zrobienie tego. Po prostu użyj nowej marki
@SpringBootTest
w swojej klasie i@MockBean
na boisku, a Spring Boot stworzy makietę tego typu i wstrzyknie ją w kontekst (zamiast wstrzykiwać oryginalną):Z drugiej strony, jeśli nie używasz Spring Boot lub poprzedniej wersji, będziesz musiał wykonać trochę więcej pracy:
Utwórz
@Configuration
fasolę, która wstrzykuje twoje makiety w kontekst wiosenny:Za pomocą
@Primary
adnotacji mówisz wiosną, że ta fasola ma priorytet, jeśli nie określono kwalifikatora.Upewnij się, że adnotujesz klasę
@Profile("useMocks")
, aby kontrolować, które klasy będą używać makiety, a które będą używać prawdziwej fasoli.Na koniec aktywuj
userMocks
profil w teście :Jeśli nie chcesz używać makiety, ale prawdziwej fasoli, po prostu nie aktywuj
useMocks
profilu:źródło
web.xml
konfiguracją oldschoolową i AnnotationConfigWebApplicationContext. Musiałem użyć@WebAppConfiguration
zamiast@WebIntegrationTest
i@ContextHierarchy
z@ContextConfiguration
zamiast@SpringApplicationConfiguration
.@Primary
adnotację do mojej sprawy, ponieważ wewnątrz nie było wywołania,@PostConstruct
które chciałem wyśmiewać, ale@PostConstruct
fasola została utworzona przed moją próbą, więc nie używała tej próby (dopóki nie dodałem@Primary
).Od wersji 1.8.3 Mockito ma
@InjectMocks
- jest to niezwykle przydatne. Moje testy JUnit są i budować obiekty, które spełniają wszystkie zależności dla klasy badanej, które są wstrzykiwane gdy członek prywatny jest opatrzone .@RunWith
MockitoJUnitRunner
@Mock
@InjectMocks
I do testów integracyjnych dopiero teraz.
@RunWith
SpringJUnit4Runner
Zwrócę uwagę, że wydaje się, że nie jest w stanie wstrzyknąć
List<T>
w taki sam sposób jak Wiosna. Szuka tylko obiektu próbnego spełniającego wymaganiaList
i nie wstrzykuje listy obiektów próbnych. Obejściem tego problemu było użycie@Spy
przeciwko ręcznie utworzonej listy i ręczne dodanie obiektów próbnych do tej listy do testowania jednostkowego. Może to było zamierzone, ponieważ z pewnością zmusiło mnie to do zwrócenia szczególnej uwagi na to, co razem szydzono.źródło
Aktualizacja: Istnieją teraz lepsze, czystsze rozwiązania tego problemu. Najpierw rozważ inne odpowiedzi.
Ostatecznie znalazłem odpowiedź na to pytanie przez ronen na jego blogu. Problem, który miałem, wynika z metody
Mockito.mock(Class c)
deklarowania typu zwracanegoObject
. W związku z tym Spring nie jest w stanie wywnioskować typu ziarna na podstawie typu zwrotu metody fabrycznej.Rozwiązaniem Ronena jest stworzenie
FactoryBean
implementacji, która zwraca kpiny.FactoryBean
Interfejs pozwala Wiosna kwerendy typu obiektów stworzonych przez fasoli fabrycznych.Moja wyśmiewana definicja fasoli wygląda teraz następująco:
źródło
Od wiosny 3.2 nie jest to już problemem. Wiosna obsługuje teraz automatyczne okablowanie wyników ogólnych metod fabrycznych. Zobacz sekcję „Ogólne metody fabryczne” w tym poście na blogu: http://spring.io/blog/2012/11/07/spring-framework-3-2-rc1-new-testing-features/ .
Kluczową kwestią jest:
Co oznacza, że powinno to działać natychmiast po wyjęciu z pudełka:
źródło
Poniższy kod działa z automatycznym okablowaniem - nie jest to najkrótsza wersja, ale przydatna, gdy powinna działać tylko ze standardowymi słoikami Spring / Mockito.
źródło
Jeśli używasz spring> = 3.0 , spróbuj użyć
@Configuration
adnotacji Springs, aby zdefiniować część kontekstu aplikacjiJeśli nie chcesz używać @ImportResource, możesz to zrobić na odwrót:
Aby uzyskać więcej informacji, zapoznaj się z dokumentacją Spring-Framework-Reference: Konfiguracja kontenera oparta na Javie
źródło
Być może nie jest to idealne rozwiązanie, ale zwykle nie używam sprężyny do wykonywania DI w testach jednostkowych. zależności dla pojedynczego komponentu bean (klasy testowanej) zwykle nie są zbyt skomplikowane, więc wykonuję wstrzyknięcie bezpośrednio w kodzie testowym.
źródło
Za pomocą Mockito mogę wykonać następujące czynności:
źródło
Opublikowanie kilku przykładów opartych na powyższych podejściach
Ze wiosną:
Bez wiosny:
źródło
Aktualizacja - nowa odpowiedź tutaj: https://stackoverflow.com/a/19454282/411229 . Ta odpowiedź dotyczy tylko wersji Spring od wersji 3.2.
Szukałem przez chwilę bardziej ostatecznego rozwiązania tego problemu. Ten post wydaje się pokrywać wszystkie moje potrzeby i nie polega na zamawianiu deklaracji fasoli. Wszystkie podziękowania dla Mattiasa Seversona. http://www.jayway.com/2011/11/30/spring-integration-tests-part-i-creating-mock-objects/
Zasadniczo zaimplementuj FactoryBean
Następnie zaktualizuj konfigurację wiosenną o:
źródło
Patrząc na tempo rozwoju Springockito i liczbę otwartych problemów , byłbym trochę zmartwiony, aby wprowadzić go teraz do mojego zestawu testów. Fakt, że ostatnie wydanie zostało zrobione przed wydaniem Spring 4, rodzi pytania: „Czy można łatwo zintegrować go z Spring 4?”. Nie wiem, bo nie próbowałem. Wolę podejście czysto wiosenne, jeśli muszę kpić z fasoli wiosennej w teście integracji.
Istnieje możliwość sfałszowania fasoli wiosennej za pomocą zwykłych funkcji wiosennych. Trzeba użyć
@Primary
,@Profile
i@ActiveProfiles
adnotacje do niego. Napisałem post na blogu na ten temat.źródło
Znalazłem podobną odpowiedź jak teabot, aby utworzyć MockFactory, który zapewnia symulacje. Użyłem następującego przykładu, aby stworzyć próbną fabrykę (ponieważ link do narkisr jest martwy): http://hg.randompage.org/java/src/407e78aa08a0/projects/bookmarking/backend/spring/src/test/java/ org / randompage / bookmarking / backend / testUtils / MocksFactory.java
Pomaga to również zapobiec sytuacji, w której Wiosna chce rozwiązać zastrzyki z wyśmiewanej fasoli.
źródło
^ działa idealnie dobrze, jeśli zadeklarowany jako pierwszy / na początku w pliku XML. Mockito 1.9.0 / Spring 3.0.5
źródło
Używam kombinacji podejścia zastosowanego w odpowiedzi przez Markusa T i prostej implementacji tego pomocnika,
ImportBeanDefinitionRegistrar
która szuka niestandardowej adnotacji (@MockedBeans
), w której można określić, które klasy mają być wyśmiewane. Uważam, że takie podejście skutkuje zwięzłym testem jednostkowym, z którego usunięto część kodu płyty wzorcowej związanej z kpiną.Oto jak wygląda przykładowy test jednostkowy z takim podejściem:
Aby tak się stało, musisz zdefiniować dwie proste klasy pomocnicze - niestandardową adnotację (
@MockedBeans
) i niestandardowąImportBeanDefinitionRegistrar
implementację.@MockedBeans
definicję adnotacji należy opatrzyć adnotacjami,@Import(CustomImportBeanDefinitionRegistrar.class)
a takżeImportBeanDefinitionRgistrar
dodać do konfiguracji fałszywą definicję fasoli w jejregisterBeanDefinitions
metodzie.Jeśli podoba Ci się to podejście, możesz znaleźć przykładowe implementacje na moim blogu .
źródło
Opracowałem rozwiązanie na podstawie propozycji Kresimira Neseka. Dodałem nową adnotację @EnableMockedBean , aby kod był nieco bardziej przejrzysty i modułowy.
Napisałem post wyjaśniający to.
źródło
Sugeruję migrację twojego projektu do Spring Boot 1.4. Następnie możesz użyć nowej adnotacji,
@MockBean
aby sfałszować swojącom.package.Dao
źródło
Dzisiaj dowiedziałem się, że wiosenny kontekst, w którym zadeklarowałem przed fasolą Mockito, nie ładował się. Po przeniesieniu PO próbkach kontekst aplikacji został pomyślnie załadowany. Dbać :)
źródło
Dla przypomnienia, wszystkie moje testy działają poprawnie, po prostu inicjalizując urządzenie leniwie, np .:
Przypuszczam, że uzasadnieniem jest to, co Mattias wyjaśnia tutaj (na dole postu), że obejście zmienia kolejność deklaracji fasoli - leniwa inicjalizacja jest „rodzajem” zadeklarowania urządzenia na końcu.
źródło
Jeśli używasz wstrzykiwania kontrolera, upewnij się, że zmienne lokalne NIE są „ostateczne”
źródło