Question

Given a legacy application has 1500 spring.xmls. I want to write a unit test for a service. I am deep in the dependency hell. I have to accept the app as is, no way out.

So we use spring-3.something and mockito-1.9 and I want the nice way to test a service. Newer code heavily use @Autowired annotation.

Indirectly, the service uses ~25 helpers (factory methods etc.) which I actually want to use in the test, and ~25 objects which I am not interested in for this test.

I currently try to setup the context in the way outlined above, but I am confused about the effects of @Mock, @InjectMocks, @Autowired.

My test is given below. I need assistance to set it up correctly.

Questions:

  • what is the actual effect of @InjectMocks ?
  • how can I decide (technically) which autowired beans are really used, and which are replaced by mocks?
  • I know, am misusing mocks to get fakes. Is there an easier way to get fakes in one-liners?
  • *Please note that I want to understand this as I have loads of such services... *

Here's my sample:

@ContextConfiguration(locations = {
    "classpath:/some/path/MainTestConfig.spring.xml"
})
@RunWith(SpringJUnit4ClassRunner.class)
public class SampleTest {

    // ***            Uninteresting Dependencies to be mocked        *** //
    @Mock Mock1 mock1;
    @Mock Mock2 mock2;

    /** Service under test */
    @Autowired
    SomeService service;

    // ***            Tightly coupled helpers to be used              *** //
    @Autowired Helper1 helper1
    @Autowired Helper2 helpr2

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

    @Test
    public void testSample() {

        // prepare dummy context
        SomeContext context = new Context();

        // define expected result
        int expectedValue = 42;

        //execute method under test, record result
        Result actualResult = service.execute(context);

        //make assertions on result
        assertTrue(actualResult.getSomething()==expectedValue);
    }

}
Was it helpful?

Solution

I wanted to post this as a separate answer since this should better address your actual question regarding @Mock, @Autowired and @InjectMocks.

@Mock: This marks a field that should be created as a mock (using mock(MyClass.class)) when MockitoAnnotations.initMocks(this) is called.

@Autowired: Marks a field that should be assigned by Spring with a bean that implements the class / interface.

@InjectMocks: Marks a field that should be CREATED by Mockito when MockitoAnnoations.initMocks(this) is called. It creates an instance of the class and injects the @Mock-annotated fields into this instance. (see correction to this statement in comments).

Analysis:

@InjectMocks is not compatible with Spring contexts and @Autowired because InjectMock creates a new instance of the class it does not use the Spring instance.

To do what you are looking for, you need to use Springockito. (delayed update) Springockito will allow you to inject mocks into your Spring context and thereby have those mocks used as Autowired candidates. It allows for Mocks and Spys. Using ReplaceWithMock and Autowired on the same field in your test is the general practice (as is shown in the example on the wiki).

OTHER TIPS

First question, do you NEED to load the spring context do this test? Generally when I have Spring projects, I have one UNIT test per class that DOES NOT load a context file. I will have a different CONTEXT test for loading the context file and verifying that it loads properly. If you are doing true unit testing I would suggest not loading the context. If the class under test using @Autowired to assign dependencies (and so has no setters) use Spring's ReflectionTestUtils to assign those fields.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top