Wstrzykiwanie Mockito w wiosenną fasolę

284

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ą @Autowiredadnotacji na prywatnych polach członkowskich.

Zastanawiałem się nad użyciem, ReflectionTestUtils.setFieldale 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-argwartość na coś nieprawidłowego, nie wystąpi błąd podczas uruchamiania kontekstu aplikacji.

teabot
źródło
4
Proszę spojrzeć na to małe stworzenie: bitbucket.org/kubek2k/springockito/wiki/Home
kubek2k
To bardzo czyste podejście - podoba mi się!
teabot
2
Miałeś mnie na adnotacjach Springockito.
yihtserns
2
Dla tych, którzy używają wiosny 4. *, od stycznia 2015 r. Nie działa to z najnowszą wiosenną wersją mockito, a projekt wydaje się być nieaktywny.
Murali

Odpowiedzi:

130

Najlepszym sposobem jest:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock"> 
    <constructor-arg value="com.package.Dao" /> 
</bean> 

Aktualizacja
W pliku kontekstowym próbka musi być wymieniona przed zadeklarowaniem dowolnego pola automatycznego w zależności od niego.

amra
źródło
Pojawia się błąd: „Błąd tworzenia fasoli o nazwie„ mockito ”: definicja fasoli jest abstrakcyjna”
tttppp
4
@amra: wiosna nie wnioskuje o typie obiektu zwróconego w tym przypadku ... stackoverflow.com/q/6976421/306488
lisak
7
Nie wiem, dlaczego ta odpowiedź jest tak wysoko oceniana, wynikowa fasola nie może być automatycznie napisana, ponieważ ma niewłaściwy typ.
azerol
4
Można go automatycznie przypisać, jeśli jest wymieniony jako pierwszy w pliku kontekstowym (zanim zadeklarowane zostaną pola zależne od niego).
Ryan Walls
3
Od wiosny 3.2 kolejność ziaren nie ma już znaczenia. Zobacz sekcję zatytułowaną „Ogólne metody fabryczne” w tym poście na blogu: spring.io/blog/2012/11/07/…
Ryan Walls,
110
@InjectMocks
private MyTestObject testObject;

@Mock
private MyDependentObject mockedObject;

@Before
public void setup() {
        MockitoAnnotations.initMocks(this);
}

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.

Greg Beauchamp
źródło
1
Jak zlikwidować określoną metodę mockedObject?
Jim Holden,
@Teinacher when (mockedObject.execute) .thenReturn (objToReturn); Możesz umieścić to w metodzie przed lub w metodzie testowej.
chaostheory
40
FYI: To podejście nie zadziała, jeśli chcę częściowe automatyczne okablowanie i częściowe kpiny w MyTestObject.
raksja
9
Nie wiem, dlaczego nie głosuje się wyżej. Jeśli zobaczę więcej odpowiedzi zawierających XML, rzucę się.
MarkOfHall
3
Dlaczego nie używacie Mockito.spy(...)na to mockedObjectzamiast? A następnie użyj when(mockedObject.execute).thenReturn(objToReturn)lub doReturn(objToReturn).when(mockedObject).execute(). Drugi nie wywołuje prawdziwej metody. Możesz również sprawdzić Mockito.doCallRealMethod()dokumentację
Tomasz Przybylski
63

Mam bardzo proste rozwiązanie przy użyciu Spring Java Config i Mockito:

@Configuration
public class TestConfig {

    @Mock BeanA beanA;
    @Mock BeanB beanB;

    public TestConfig() {
        MockitoAnnotations.initMocks(this); //This is a key
    }

    //You basically generate getters and add @Bean annotation everywhere
    @Bean
    public BeanA getBeanA() {
        return beanA;
    }

    @Bean
    public BeanB getBeanB() {
        return beanB;
    }
}
Piotr Gwiazda
źródło
4
Z jakiegoś powodu dzięki takiemu podejściu wiosna i tak próbuje stworzyć rzeczywistą fasolę (zamiast udawać) i dusi się ... Co robię źle?
Daniel Gruszczyk
1
Mam ten sam problem
Korobko Alex
3
Nie wiosną, ale raczej mockito próbuje stworzyć prawdziwą fasolę, jeśli wyśmiewasz klasę. Jeśli masz jakieś komponenty bean, które muszą być wyśmiewane w testach, powinny to być implementacje interfejsu i wstrzykiwane przez ten interfejs. Jeśli następnie wyśmiejesz interfejs (a nie klasę), mockito nie będzie próbował utworzyć instancji tej klasy.
Daniel Gruszczyk
7
O co chodzi? Po co dodawać pola z adnotacjami i konstruktor initMocks? Dlaczego nie tylko return Mockito.mock(BeanA.class)w środku getBeanA? W ten sposób jest to prostsze i jest mniej kodu. czego mi brakuje?
Oleg
1
@Oleg Wygląda na to, że masz własne rozwiązanie, które prawdopodobnie powinieneś opublikować jako odpowiedź, aby społeczność mogła na niego głosować.
Dawood ibn Kareem,
48

Dany:

@Service
public class MyService {
    @Autowired
    private MyDAO myDAO;

    // etc
}

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.

@ContextConfiguration(classes = { MvcConfiguration.class })
@RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
    @Autowired
    private MyService myService;

    private MyDAO myDAOMock;

    @Before
    public void before() {
        myDAOMock = Mockito.mock(MyDAO.class);
        ReflectionTestUtils.setField(myService, "myDAO", myDAOMock);
    }

    // etc
}

Należy pamiętać, że przed wiosną 4.3.1 ta metoda nie będzie działać z usługami za proxy (z adnotacjami @Transactionallub Cacheable, 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.setFieldteraz robi domyślnie)

Paul Croarkin
źródło
Podwójnie @RunWith (SpringJUnit4ClassRunner.class) i używam różnych adnotacji dla klasy testowej (ten sam biegacz), ale takie podejście działa dla mnie, dzięki.
user1317422
1
Inspirowało mnie bardzo „Uwaga: przed wiosną 4.3.1 ta metoda nie będzie działać z usługami za serwerem proxy (na przykład z @Transactional lub Cacheable). Zostało to naprawione przez SPR-14050”. Właśnie natrafiłem na ten problem i nie dostałem żadnych wskazówek, dopóki nie zauważyłem tych słów. WIELKIE DZIĘKI!
snowfox,
1
To rozwiązanie jest obsługiwane, gdy podłączyłeś cały kontekst aplikacji i, dla celów testowych, chcesz wstrzyknąć próbkę losowej fasoli w twoim kontekście. Użyłem tej odpowiedzi, aby kpić z udawanej fasoli klienta, aby uniknąć wywołań REST do innych modułów w teście modułu. Adnotacja InjectMock działa tylko wtedy, gdy wstrzykujesz makiety w fasoli, którą zamierzasz przetestować, a nie w fasoli utworzonej przez konfigurację aplikacji Spring.
Andreas Lundgren,
1
Prawie cały dzień kręci się wokół, próbując sprawić, że @MockBean działa bez resetowania kontekstu, a potem napotykam ten klejnot. Dokładnie to, czego potrzebowałem, na zdrowie.
Matt R
Działa, choć należy pamiętać, że zastąpione pole może nie zostać zresetowane z powodu buforowania i niektóre niepowiązane testy mogą ulec awarii. Np. W moim teście wymieniłem koder haseł na próbny, a kilka innych testów zostało przerwanych z powodu błędów autoryzacji.
alextsil,
36

Jeśli używasz Spring Boot 1.4, jest to świetny sposób na zrobienie tego. Po prostu użyj nowej marki @SpringBootTestw swojej klasie i @MockBeanna boisku, a Spring Boot stworzy makietę tego typu i wstrzyknie ją w kontekst (zamiast wstrzykiwać oryginalną):

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {

    @MockBean
    private RemoteService remoteService;

    @Autowired
    private Reverser reverser;

    @Test
    public void exampleTest() {
        // RemoteService has been injected into the reverser bean
        given(this.remoteService.someCall()).willReturn("mock");
        String reverse = reverser.reverseSomeCall();
        assertThat(reverse).isEqualTo("kcom");
    }

}

Z drugiej strony, jeśli nie używasz Spring Boot lub poprzedniej wersji, będziesz musiał wykonać trochę więcej pracy:

Utwórz @Configurationfasolę, która wstrzykuje twoje makiety w kontekst wiosenny:

@Configuration
@Profile("useMocks")
public class MockConfigurer {

    @Bean
    @Primary
    public MyBean myBeanSpy() {
        return mock(MyBean.class);
    }
}

Za pomocą @Primaryadnotacji 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 userMocksprofil w teście :

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
@ActiveProfiles(profiles={"useMocks"})
public class YourIntegrationTestIT {

    @Inject
    private MyBean myBean; //It will be the mock!


    @Test
    public void test() {
        ....
    }
}

Jeśli nie chcesz używać makiety, ale prawdziwej fasoli, po prostu nie aktywuj useMocksprofilu:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
public class AnotherIntegrationTestIT {

    @Inject
    private MyBean myBean; //It will be the real implementation!


    @Test
    public void test() {
        ....
    }
}
jfcorugedo
źródło
5
Ta odpowiedź powinna znaleźć się na górze - wsparcia @MockBean w wiosennym rozruchu można również używać bez rozruchu wiosennego. Można go używać tylko w testach jednostkowych, dzięki czemu działa we wszystkich aplikacjach wiosennych!
bedrin
2
Adnotacja @Profile, którą możesz ustawić również dla metody definicji komponentu bean, aby uniknąć tworzenia osobnej klasy konfiguracji
marcin
Świetna odpowiedź! Wprowadziłem kilka zmian, aby działał z moją web.xmlkonfiguracją oldschoolową i AnnotationConfigWebApplicationContext. Musiałem użyć @WebAppConfigurationzamiast @WebIntegrationTesti @ContextHierarchyz @ContextConfigurationzamiast @SpringApplicationConfiguration.
UTF_or_Death
Musiałem dodać @Primaryadnotację do mojej sprawy, ponieważ wewnątrz nie było wywołania, @PostConstructktóre chciałem wyśmiewać, ale @PostConstructfasola została utworzona przed moją próbą, więc nie używała tej próby (dopóki nie dodałem @Primary).
helleye
19

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 .@RunWithMockitoJUnitRunner@Mock@InjectMocks

I do testów integracyjnych dopiero teraz.@RunWithSpringJUnit4Runner

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 wymagania Listi nie wstrzykuje listy obiektów próbnych. Obejściem tego problemu było użycie @Spyprzeciwko 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.

Doug Moscrop
źródło
Tak, to najlepszy sposób. Springockito tak naprawdę nie wstrzykuje kpiny z jakiegokolwiek powodu w moim przypadku.
chaostheory,
13

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 zwracanego Object. W związku z tym Spring nie jest w stanie wywnioskować typu ziarna na podstawie typu zwrotu metody fabrycznej.

Rozwiązaniem Ronena jest stworzenie FactoryBeanimplementacji, która zwraca kpiny. FactoryBeanInterfejs pozwala Wiosna kwerendy typu obiektów stworzonych przez fasoli fabrycznych.

Moja wyśmiewana definicja fasoli wygląda teraz następująco:

<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>
teabot
źródło
1
Zaktualizowany link do rozwiązania Ronena
Jeff Martin
Nie rozumiem tego, metoda fabryczna ma typ zwrotu Obiekt ... Ale rozwiązanie amry ma ogólny typ zwrotu, więc Wiosna powinna ją rozpoznać ... Ale rozwiązanie amry nie działa dla mnie
lisak
Ani to rozwiązanie, wiosna nie wnioskuje o rodzaju fasoli, która jest zwracana z fabryki, dlatego nie ma pasującej fasoli typu [com.package.exe] ...
lisak,
1
Droga powrotna: web.archive.org/web/20120806223839/http://…
Daniel Kaplan
Ten link nadal działa: javadevelopmentforthemasses.blogspot.com/2008/07 /... Wyłącz po prostu przekierowanie linku w przeglądarce, a zobaczysz go, zamiast zmuszać się do przeglądania 404 na jego nowym blogu.
około
12

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:

Wiosną 3.2 ogólne typy zwrotów dla metod fabrycznych są teraz poprawnie wywnioskowane, a automatyczne okablowanie według typu dla prób powinno działać zgodnie z oczekiwaniami. W rezultacie niestandardowe obejścia, takie jak MockitoFactoryBean, EasyMockFactoryBean lub Springockito, prawdopodobnie nie są już potrzebne.

Co oznacza, że ​​powinno to działać natychmiast po wyjęciu z pudełka:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean>
Ryan Walls
źródło
9

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.

<bean id="dao" class="org.springframework.aop.framework.ProxyFactoryBean">
   <property name="target"> <bean class="org.mockito.Mockito" factory-method="mock"> <constructor-arg value="com.package.Dao" /> </bean> </property>
   <property name="proxyInterfaces"> <value>com.package.Dao</value> </property>
</bean> 
Kamil
źródło
Pracował dla mnie. Musiałem rozpakować proxy w teście, aby je zweryfikować zgodnie z opisem tutaj: forum.spring.io/forum/spring-projects/aop/…
Holgzn
9

Jeśli używasz spring> = 3.0 , spróbuj użyć @Configurationadnotacji Springs, aby zdefiniować część kontekstu aplikacji

@Configuration
@ImportResource("com/blah/blurk/rest-of-config.xml")
public class DaoTestConfiguration {

    @Bean
    public ApplicationService applicationService() {
        return mock(ApplicationService.class);
    }

}

Jeśli nie chcesz używać @ImportResource, możesz to zrobić na odwrót:

<beans>
    <!-- rest of your config -->

    <!-- the container recognize this as a Configuration and adds it's beans 
         to the container -->
    <bean class="com.package.DaoTestConfiguration"/>
</beans>

Aby uzyskać więcej informacji, zapoznaj się z dokumentacją Spring-Framework-Reference: Konfiguracja kontenera oparta na Javie

Markus T.
źródło
Niezłe. Użyłem tego, gdy testowany test jest @Autowired w rzeczywistym przypadku testowym.
enkor
8

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.

Angelo Genovese
źródło
3
Rozumiem twoje podejście. Jednak znajduję się w tej sytuacji z dużą bazą kodu, która nie pozwala na to łatwo - na razie.
teabot
1
Przekonałem się, że kombinacja Mockito / Spring jest bardzo przydatna, gdy muszę przetestować kod, który zależy w dużej mierze od aspektów Spring / AOP (na przykład podczas testowania reguł bezpieczeństwa Spring). Chociaż jest całkowicie uzasadnione twierdzenie, że takie testy powinny być testem integracyjnym.
Lars Tackmann
@Lars - zgodził się - to samo można powiedzieć o testach, z którymi mam do czynienia.
teabot
7

Za pomocą Mockito mogę wykonać następujące czynności:

<bean id="stateMachine" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.abcd.StateMachine"/>
</bean>
Alexander
źródło
1
Dzięki za odpowiedź @Alexander. Czy mogę zapytać: czy prawidłowo się łączy? Jeśli tak, jakich wersji Spring / Mockito używasz?
teabot
6

Opublikowanie kilku przykładów opartych na powyższych podejściach

Ze wiosną:

@ContextConfiguration(locations = { "classpath:context.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public class TestServiceTest {
    @InjectMocks
    private TestService testService;
    @Mock
    private TestService2 testService2;
}

Bez wiosny:

@RunWith(MockitoJUnitRunner.class)
public class TestServiceTest {
    @InjectMocks
    private TestService testService = new TestServiceImpl();
    @Mock
    private TestService2 testService2;
}
Basu
źródło
2

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

package com.jayway.springmock;

import org.mockito.Mockito;
import org.springframework.beans.factory.FactoryBean;

/**
 * A {@link FactoryBean} for creating mocked beans based on Mockito so that they 
 * can be {@link @Autowired} into Spring test configurations.
 *
 * @author Mattias Severson, Jayway
 *
 * @see FactoryBean
 * @see org.mockito.Mockito
 */
public class MockitoFactoryBean<T> implements FactoryBean<T> {

    private Class<T> classToBeMocked;

    /**
     * Creates a Mockito mock instance of the provided class.
     * @param classToBeMocked The class to be mocked.
     */
    public MockitoFactoryBean(Class<T> classToBeMocked) {
        this.classToBeMocked = classToBeMocked;
    }

    @Override
    public T getObject() throws Exception {
        return Mockito.mock(classToBeMocked);
    }

    @Override
    public Class<?> getObjectType() {
        return classToBeMocked;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

Następnie zaktualizuj konfigurację wiosenną o:

<beans...>
    <context:component-scan base-package="com.jayway.example"/>

    <bean id="someDependencyMock" class="com.jayway.springmock.MockitoFactoryBean">
        <constructor-arg name="classToBeMocked" value="com.jayway.example.SomeDependency" />
    </bean>
</beans>
Ryan Walls
źródło
2

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, @Profilei @ActiveProfilesadnotacje do niego. Napisałem post na blogu na ten temat.

luboskrnac
źródło
1

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

<bean id="someFacade" class="nl.package.test.MockFactory">
    <property name="type" value="nl.package.someFacade"/>
</bean>

Pomaga to również zapobiec sytuacji, w której Wiosna chce rozwiązać zastrzyki z wyśmiewanej fasoli.

Renso Lohuis
źródło
1
<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>

^ działa idealnie dobrze, jeśli zadeklarowany jako pierwszy / na początku w pliku XML. Mockito 1.9.0 / Spring 3.0.5

migdałowiec
źródło
1

Używam kombinacji podejścia zastosowanego w odpowiedzi przez Markusa T i prostej implementacji tego pomocnika, ImportBeanDefinitionRegistrarktó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:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class ExampleServiceIntegrationTest {

    //our service under test, with mocked dependencies injected
    @Autowired
    ExampleService exampleService;

    //we can autowire mocked beans if we need to used them in tests
    @Autowired
    DependencyBeanA dependencyBeanA;

    @Test
    public void testSomeMethod() {
        ...
        exampleService.someMethod();
        ...
        verify(dependencyBeanA, times(1)).someDependencyMethod();
    }

    /**
     * Inner class configuration object for this test. Spring will read it thanks to
     * @ContextConfiguration(loader=AnnotationConfigContextLoader.class) annotation on the test class.
     */
    @Configuration
    @Import(TestAppConfig.class) //TestAppConfig may contain some common integration testing configuration
    @MockedBeans({DependencyBeanA.class, DependencyBeanB.class, AnotherDependency.class}) //Beans to be mocked
    static class ContextConfiguration {

        @Bean
        public ExampleService exampleService() {
            return new ExampleService(); //our service under test
        }
    }
}

Aby tak się stało, musisz zdefiniować dwie proste klasy pomocnicze - niestandardową adnotację ( @MockedBeans) i niestandardową ImportBeanDefinitionRegistrarimplementację. @MockedBeansdefinicję adnotacji należy opatrzyć adnotacjami, @Import(CustomImportBeanDefinitionRegistrar.class)a także ImportBeanDefinitionRgistrardodać do konfiguracji fałszywą definicję fasoli w jej registerBeanDefinitionsmetodzie.

Jeśli podoba Ci się to podejście, możesz znaleźć przykładowe implementacje na moim blogu .

Krešimir Nesek
źródło
1

Opracowałem rozwiązanie na podstawie propozycji Kresimira Neseka. Dodałem nową adnotację @EnableMockedBean , aby kod był nieco bardziej przejrzysty i modułowy.

@EnableMockedBean
@SpringBootApplication
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes=MockedBeanTest.class)
public class MockedBeanTest {

    @MockedBean
    private HelloWorldService helloWorldService;

    @Autowired
    private MiddleComponent middleComponent;

    @Test
    public void helloWorldIsCalledOnlyOnce() {

        middleComponent.getHelloMessage();

        // THEN HelloWorldService is called only once
        verify(helloWorldService, times(1)).getHelloMessage();
    }

}

Napisałem post wyjaśniający to.

Alfredo Diaz
źródło
1

Sugeruję migrację twojego projektu do Spring Boot 1.4. Następnie możesz użyć nowej adnotacji, @MockBeanaby sfałszować swojącom.package.Dao

luboskrnac
źródło
0

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ć :)

Daniele Dellafiore
źródło
1
Czegoś brakuje. 8-) Co przeniosłeś po próbach?
Hans-Peter Störr
0

Dla przypomnienia, wszystkie moje testy działają poprawnie, po prostu inicjalizując urządzenie leniwie, np .:

<bean id="fixture"
      class="it.tidalwave.northernwind.rca.embeddedserver.impl.DefaultEmbeddedServer"
      lazy-init="true" /> <!-- To solve Mockito + Spring problems -->

<bean class="it.tidalwave.messagebus.aspect.spring.MessageBusAdapterFactory" />

<bean id="applicationMessageBus"
      class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="it.tidalwave.messagebus.MessageBus" />
</bean>

<bean class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="javax.servlet.ServletContext" />
</bean>

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.

Fabrizio Giudici
źródło
-1

Jeśli używasz wstrzykiwania kontrolera, upewnij się, że zmienne lokalne NIE są „ostateczne”

RS
źródło