6

My question is very similar to the issue raised in Injecting Mockito mocks into a Spring bean. In fact, I believe the accepted answer there might actually work for me. However, I've got one issue with the answer, and then some further explanation in case the answer there is not in fact my answer.

So I followed the link in the aforementioned post to the Springockito website. I altered my test-config.xml to include something similar to the following:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mockito="http://www.mockito.org/spring/mockito"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.mockito.org/spring/mockito http://www.mockito.org/spring/mockito.xsd">

...

    <mockito:mock id="accountService" class="org.kubek2k.account.DefaultAccountService" />
...
</beans>

There seems to be something wrong with the www.mockito.org redirect currently, so I found the XSD code at https://bitbucket.org/kubek2k/springockito/raw/16143b32095b/src/main/resources/spring/mockito.xsd and altered the final entry in xsi:schemaLocation to point to this bitbucket link.

Running mvn test then produced the following error (newlines added for readability):

Caused by: org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException:
    Line 43 in XML document from class path resource [spring/test-context.xml] is invalid;
    nested exception is org.xml.sax.SAXParseException; lineNumber: 43; columnNumber: 91;
    The prefix "mockito" for element "mockito:mock" is not bound.

So the question regarding Springockito is: Is it possible anymore to include this? What am I missing?

Now, on to the further explanation...

I have an interface whose implementation I'm trying to test:

public interface MobileService {
    public Login login(Login login);
    public User getUser(String accessCode, Date birthDate);
}

The implementation contains a DAO that Spring @Autowires in for me:

@Service
public class MobileServiceImpl implements MobileService {
    private MobileDao mobileDao;

    @Autowired
    public void setMobileDao(MobileDao mobileDao) {
        this.mobileDao = mobileDao;
    }
}

I don't want to alter my interface to include a setMobileDao method, because that would be adding code just to support my unit testing. I'm trying to mock out the DAO since the actual SUT here is the ServiceImpl. How can I achieve this?

Community
  • 1
  • 1
Mike
  • 7,994
  • 5
  • 35
  • 44

5 Answers5

9

You don't want to test your interface: it contains no code at all. You want to test your implementation. So the setter is available. Just use it:

@Test
public void testLogin() {
    MobileServiceImpl toTest = new MobileServiceImpl();
    toTest.setMobileDao(mockMobileDao);
    // TODO call the login method and check that it works as expected.
}

No need for a spring context. Just instanciate your POJO service, inject mock dependencies manually, and test the methods you want to test.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • 1
    @Mike I agree, just test the implementation. There's no point to have spring context for a **unit** test. And by the way Mockito offers some trivial dependency injection mechanism with a combination of `@Mock` and `@InjectMocks` annotations. You should see their respective [javadoc](http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html). – bric3 Oct 19 '11 at 07:54
1

After struggling with the Springockito XSD issue, for a while, I found a much simpler solution. Let Spring inject the mock for you using a factory method, i.e. in applicationContext.xml put:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.gerrydevstory.mycoolbank.AccountsDAO"/>
  </bean>

  <bean class="com.gerrydevstory.mycoolbank.BankingService"/>

</beans>

where the AccountsDAO bean is injected into the BankingService class. The corresponding JUnit test case is:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/BankingServiceTest.xml")
public class BankingServiceTest {

  @Autowired private BankingService bankingService;
  @Autowired private AccountsDAO mockAccountsDAO;

  @Test
  public void testTransfer() throws Exception {
    // Setup 2 accounts
    Account acc1 = new Account();
    acc1.setBalance(800.00);
    Account acc2 = new Account();
    acc2.setBalance(200.00);

    // Tell mock DAO to return above accounts when 1011 or 2041 is queried respectively
    when(mockAccountsDAO.findById(1011)).thenReturn(acc1);
    when(mockAccountsDAO.findById(2041)).thenReturn(acc2);

    // Invoke the method to test
    bankingService.transfer(1011, 2041, 500.00);

    // Verify the money has been transferred
    assertEquals(300.00, acc1.getBalance(), 0.001);
    assertEquals(700.00, acc2.getBalance(), 0.001);
  }
}

Personally I find this very elegant and easy to understand. For more details, see the original blog post.

Erica Kane
  • 3,137
  • 26
  • 36
0

You have three options to set your mock dao:

  1. Test the implementation - which gives a seam for your mock via the setDao method. (as JB's answer)
  2. Add the setDao method to the interface - not desired since you don't want to add code just to support your tests.
  3. Add a constructor to the impl class to accept the dao - not desired for same reason as #2.

If you wanted to do #3, you'll need to add a constructor to the MobileService that accepts the MobileDao.

   public MobileServiceImpl(MobileDao mobileDao) {
    this.mobileDao = mobileDao;
}

Then your test will look like this:

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.*;

import java.util.Date;

import org.junit.Before;
import org.junit.Test;

public class MobileServiceImplTest {

    private MobileService systemUnderTest;

    private MobileDao mobileDao;

    @Before
    public void setup() {
        mobileDao = mock(MobileDao.class);
        systemUnderTest = new MobileServiceImpl(mobileDao);
    }

    @Test
    public void testGetUser() {
        //if you need to, configure mock behavior here. 
        //i.e. when(mobileDao.someMethod(someObject)).thenReturn(someResponse);
        systemUnderTest.getUser("accessCode", new Date());

        verify(mobileDao).getUser("JeffAtwood");
    }
}

Please note that you have not provided us with the details of the MobileDao so I created a getUser method that accepts a String.

To make the test pass, your MobileServiceImpl would just need this:

mobileDao.getUser("JeffAtwood");
Tony R
  • 7,136
  • 4
  • 21
  • 12
  • Just like adding a `setMobileDao` to the interface, adding another ctor to pass in the DAO object would be altering code to support unit tests. As per @jb-nizet 's answer, if I alter my test to not use the interface but rather the implementation, then I already have access to the `setMobileDao` method. – Mike Oct 19 '11 at 19:07
  • Understood that you don't want to add anything to the code just to support tests. Testing the impl instead of the interface is something that I've done in the past as well ;) Just wanted to give another option. – Tony R Oct 28 '11 at 15:20
0

The problem looks like your classpath doesn't contain actual springockito jar - You don't have to change the URL's - these are only the tokens that are used internally by spring - they are not being resolved - all You need is a new enough Spring distribution and springockito on classpath.

Kuba (creator of aforementioned lib :) )

kubek2k
  • 5,613
  • 2
  • 22
  • 16
0

I had same problem, I wanted use springockito by according their wiki but validations xml throw errors. So when i tried go to locations where xsd supposed to be, that hasn't. So i read this and get to working with this:

xmlns:mockito="http://www.mockito.org/spring/mockito"
xsi:schemaLocation="http://www.mockito.org/spring/mockito 
    https://bitbucket.org/kubek2k/springockito/raw/16143b32095b/src/main/resources/spring/mockito.xsd">

But when i looking at this link, had bad feeling. It seems to me that's not permanent stable link (you must check if i wrong)

Perlos
  • 2,028
  • 6
  • 27
  • 37