12

I'm trying to replace an @Autowired object with a Mockito mock object. The usual way of doing this was with xml using Springockito:

<mockito:mock id="SomeMock" class="com.package.MockInterface" />

Currently I'm trying to move over to using Spring's JavaConfig to do the job. All of a sudden the Java expressions are a whole lot more verbose than xml:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class MyTestClass {

    @Configuration
    static class Config {
        @Bean
        public MockInterface somethingSpecial() {
            return Mockito.mock(MockInterface.class);
        }
    }

    @Autowired MockInterface mockObj;

    // test code
}

I discovered a library called Springockito-annotations, which allows you to do the following:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=SpringockitoContextLoader.class)
public class MyTestClass {

    @Autowired @ReplaceWithMock MockInterface mockObj;

    // test code
}

Clearly, a whole lot prettier :) The only problem is that this context loader doesn't allow me to use @Configuration and JavaConfig for other beans (if I do, Spring complains that there are no candidates that match those autowired fields).

Do you guys know of a way to get Spring's JavaConfig and Springockito-annotations to play nice? Alternatively, is there another shorthand for creating mocks?

As a nice bonus, using Springockito and xml config, I was able to mock out concrete classes without providing autowiring candidates to its dependencies (if it had any). Is this not possible without xml?

MariuszS
  • 30,646
  • 12
  • 114
  • 155
jabalsad
  • 2,351
  • 6
  • 23
  • 28
  • Perhaps I am a bit confused, but why don't you `@Autowired` the beans you want from your application context, and `@Mock` the beans you want mocked? Note: if you use `@Mock`, you will need to initialize those mocks by calling `MockitoAnnotations.initMocks(this)` in your test class before each test. – nicholas.hauschild Feb 15 '13 at 17:16
  • 1
    why don't you simply wire this by hand for your test? – bowmore Feb 16 '13 at 07:36
  • If I want to wire them by hand, I need to change the class to allow me to inject the mock objects. What Springockito allows you to do is to use the existing autowiring mechanism to inject mocks. – jabalsad Feb 18 '13 at 18:08
  • Why would you have to change the class? If it's injected through constructor, you just need to call the constructor. If it's injected by setter, you just need to call the setter. And if it's using field injection, you just need to use the `@Mock` and `@InjectMocks` annotations in your test (which would also work with constructor and setter injection). – JB Nizet Jan 11 '14 at 22:49

3 Answers3

4

Moving away from the now unmaintained (as of this writing) Spingockito-annotations and to Mockito, we have a way of doing this very simply:

@RunWith(MockitoJUnitRunner.class)
@ContextConfiguration
public class MyTestClass {

    @Mock MockInterface mockObj;

    // test code
}

If you're using a real object, but would like to mock a dependency within it, for instance testing a service layer with DAO:

@RunWith(MockitoJUnitRunner.class)
@ContextConfiguration
public class MyTestClass {

    @InjectMocks RealService;

    @Mock MockDAO mockDAO;

    // test code
}

Finally, this can also be applied to Spring-boot, but using annotation initialization within setUp() until multiple class runners are supported:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MyMainSpringBootClass.class)
public class MyTestClass {

    @InjectMocks RealService;

    @Mock MockDAO mockDAO;

    @Before
    public final void setUp() throws Exception{
        MockitoAnnotations.initMocks(this);
    }

    // test code
}
kryger
  • 12,906
  • 8
  • 44
  • 65
ethesx
  • 1,339
  • 5
  • 19
  • 35
2

Outdated and deprecated!

Read about mocking and spying in Spring Boot 1.4

Please read also @ethesx answer, Springockito is unmaintaned

Old answer

This is possible now to mock Spring application without any XML file with Springockito-annotations.. This solution works also with Spring Boot.

import static org.mockito.BDDMockito.*;
import org.kubek2k.springockito.annotations.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Application.class, 
     loader = SpringockitoAnnotatedContextLoader.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class MainControllerTest {

    @Autowired
    MainController mainController;

    @Autowired
    @ReplaceWithMock
    FooService fooService;

    @Test
    public void shouldGetBar() {
        //given
        given(fooService.result("foo")).willReturn("bar");

        //when
        Bar bar build = fooService.getBar("foo");

        //then
        assertThat(bar).isNotNull();
    }
}

Dependencies: org.kubek2k:springockito-annotations:1.0.9

MariuszS
  • 30,646
  • 12
  • 114
  • 155
  • 1
    I'm not seeing how this works with Spring-boot. When using your method spring-boot does not get initialized. – Shane Feb 26 '14 at 17:49
1

It appears that SpringockitoContextLoader extends GenericXmlContextLoader which is described as:

Concrete implementation of AbstractGenericContextLoader that reads bean definitions from XML resources.

So you are limited to xml bean definitions at the moment.

You could write your own context loader, taking relevant parts from the SpringockitoContextLoader class. Take a look here to get started, perhaps you could extend AnnotationConfigContextLoader for example?

Jonathan
  • 20,053
  • 6
  • 63
  • 70