20

Here's my question:

I have several web services classes to test that all inherit their methods from a generic service. Rather than write a unit test for each, I figure I can break the test suite down by functional areas (i.e. three groups of test methods, each relying on a different underlying DAO method call).

What I propose to do is:

@Mock StateDAO mockedStateDao;
@Mock CountyDAO mockedCountyDao;
@Mock VisitorDAO mockedVisitorDao;

then call:

@InjectMocks CountyServiceImpl<County> countyService = new CountyServiceImpl<County>();
@InjectMocks StateServiceImpl<State> stateService = new StateServiceImpl<State>();
@InjectMocks VisitorServiceImpl<Visitor> visitorService = new VisitorServiceImpl<Visitor>();

How can I be sure that each mockedDAO will be injected into the correct service? Would it be easier to autowire all three (rather than use @InjectMocks)?

I'm using Spring, Hibernate, and Mockito...

DYezek
  • 324
  • 1
  • 3
  • 7
  • Will be doing this inside one test class. – DYezek Mar 05 '13 at 16:17
  • Also, I have the items for @InjectMocks already in the Spring test-application-context.xml file (so I could autowire them). Not sure on the difference between autowiring and injecting mocks. – DYezek Mar 05 '13 at 16:18
  • 2
    Edit your original question to add additional content instead of leaving comments on your question. – Sled Mar 05 '13 at 18:35

4 Answers4

22

Well nicholas answer is almost correct, but instead of guessing just look at the javadoc of InjectMocks, it contains more details ;)

To me it's weird to have so many Service in a single test, it doesn't feel right, as a unit test or as an integration test. In unit test it's wrong because well you have way too much collaborators, it doesn't look like object oriented (or SOLID). In integration tests, it's weird because the code you test the integration with the DB not mock it.

For a rapid reference in 1.9.5 you have :

Mark a field on which injection should be performed.

Allows shorthand mock and spy injection. Minimizes repetitive mock and spy injection. Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection in order and as described below. If any of the following strategy fail, then Mockito won't report failure; i.e. you will have to provide dependencies yourself.

  1. Constructor injection; the biggest constructor is chosen, then arguments are resolved with mocks declared in the test only.

    Note: If arguments can not be found, then null is passed. If non-mockable types are wanted, then constructor injection won't happen. In these cases, you will have to satisfy dependencies yourself.

  2. Property setter injection; mocks will first be resolved by type, then, if there is several property of the same type, by the match of the property name and the mock name.

    Note 1: If you have properties with the same type (or same erasure), it's better to name all @Mock annotated fields with the matching properties, otherwise Mockito might get confused and injection won't happen.

    Note 2: If @InjectMocks instance wasn't initialized before and have a no-arg constructor, then it will be initialized with this constructor.

  3. Field injection; mocks will first be resolved by type, then, if there is several property of the same type, by the match of the field name and the mock name.

    Note 1: If you have fields with the same type (or same erasure), it's better to name all @Mock annotated fields with the matching fields, otherwise Mockito might get confused and injection won't happen.

    Note 2: If @InjectMocks instance wasn't initialized before and have a no-arg constructor, then it will be initialized with this constructor.

second
  • 4,069
  • 2
  • 9
  • 24
bric3
  • 40,072
  • 9
  • 91
  • 111
3

If you have multiple Services and would like to replace the DAOs with Mock-Objects in a Spring-based environment, I would recommend to use Springockito: https://bitbucket.org/kubek2k/springockito/wiki/Home

which is also mentioned here: Injecting Mockito mocks into a Spring bean

Your Testclass then might look like this:

@RunWith (SpringJUnit4ClassRunner.class)
@ContextConfiguration (loader = SpringockitoContextLoader.class, locations =    {"classpath:/org/example/package/applicationContext.xml"})
public class NameOfClassTest {

    @Autowired
    @ReplaceWithMock 
    StateDAO mockedStateDao;

    @Autowired
    @ReplaceWithMock 
    CountyDAO mockedCountyDao;

    @Autowired
    @ReplaceWithMock 
    VisitorDAO mockedVisitorDao;

In your @Test or @Before Methode you can setup your mocks the standard Mockito way:

Mockito.doReturn(null).when(mockedCountyDao).selectFromDB();
Community
  • 1
  • 1
giesemic
  • 71
  • 4
  • bitbucket link is dead. A test with a mix of real&mocked beans can nowadays be achieved with Springs own @MockBean annotation – julaine Jan 09 '23 at 15:10
1

Well, the static method MockitoAnnotations.initMocks(Object) is used to bootstrap the whole process.

I don't know for sure how it works, as I haven't browsed the source code, but I would implement it something like this:

  1. Scan the passed Object's class for member variables with the @Mock annotation.
  2. For each one, create a mock of that class, and set it to that member.
  3. Scan the passed Object's class for member variables with the @InjectMocks annotation.
  4. Scan the class of each found member for members it has that can be injected with one of the mock objects created in (2) (that is, where the field is a parent class/interface, or the same class, as the mock objects declared class) and set it to that member.
second
  • 4,069
  • 2
  • 9
  • 24
nicholas.hauschild
  • 42,483
  • 9
  • 127
  • 120
0

Nevermind, looked online- the InjectMocks annotation treats anything with the @Mock annotation as a field and is static-scoped (class wide), so I really couldn't guarentee that the mocks would go to the correct service. this was somewhat a thought experiment for trying to unit test at feature level rather than class level. Guess I'll just autowire this stuff with Spring...

DYezek
  • 324
  • 1
  • 3
  • 7