Julio Indriago
Julio Indriago

Reputation: 85

How do I unit-test Maven multi-module Spring app?

I'm having problems trying to unit test a maven multi-module project with Spring.

I've 4 modules:

application-core
application-data
application-service
application-web

This is my test, its in the application-core module:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/config/application-context.xml")
public class TicketTest {

    @Mock
    ITicketDAO ticketDAO;

    @Autowired
    @InjectMocks
    ITicketCore ticketCore;

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

    @Test
    public void testRegisterTicket_Ticket_NotUsed_isValid() {
        Long ticketNumber = 0L;
        when(ticketDAO.getTicket(anyLong())).thenReturn(null);
        final boolean isValidTicket = ticketCore.validateTicket(ticketNumber);
        assertTrue(isValidTicket);
    }

}

And here is the implementation:

@Component
@Scope("prototype")
public class TicketCore implements ITicketCore{
    private ITicketDAO ticketDao;

    @Autowired
    public TicketCore(ITicketDAO ticketDao) {
        this.ticketDao = ticketDao;
    }

    @Override
    public boolean validateTicket(Long ticketNumber) {
        ITicket ticket = ticketDao.getTicket(ticketNumber);
        return ticket != null;
    }
}

Interface:

public interface ITicketDAO {
    ITicket getTicket(Long ticketNumber);
}

Implementation of ITicketDAO its on the application-data module:

@Service
public class TicketDAO implements ITicketDAO {
    @Override
    public ITicket getTicket(Long ticketNumber) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }
}

I'm having problems testing this code because the context doesn't find the implementation of ITicketDAO. It's seems obvious, because when the test are running, JUnit doesn't care about putting in the classpath the "others modules".

Spring throws BeanCreationException for obvious reasons.

Am I right?

So I would like to test my project without Spring getting in the tests' way.

What could I do to get my tests run w/o any problem???

I've created dummy classes in the test folder/packages, and it works, but...

I would eventually have ALL the external implementations in my application-core module's test folder.

There is a better way?

Thanks in advance.

UPDATE:

application-data
application-service
application-web

all of them depends on application-core. Spring successfully inject TicketCore(application-core). What I want is to give Spring "something" (a dummy class) to inject in ITicketDAO just to run the test.

<beans>
    <context:component-scan base-package="ve.gov.imat.transimat" />
    <context:annotation-config />

    <aop:config proxy-target-class="true" />
</beans>

Upvotes: 3

Views: 3702

Answers (2)

diegomtassis
diegomtassis

Reputation: 3667

If I've understood you well your problem is your context file references a class you don't have available in your test classpath.

In principle tests shouldn't need the implementation of any collaborator to work, only the one of the sut.

One solution is to create an application-test-context.xml file under your test/resources folder to be used in your test instead of the production one. Within this file you can create the mocks of your collaborators

<!-- Mock service for splitting jobs -->
<bean factory-bean="mockControl" factory-method="createMock"
    primary="true">
    <constructor-arg value="net.compart.docpilot.model.service.JobSplitService" />
</bean>

Upvotes: 0

Pretend that each Maven module is a completely separate project. Put tests specifically of each module's code inside it, and add integration tests in the module where all of the dependencies necessary to run them have been included.

You haven't provided any information on the dependencies between your modules, but it appears that the problem you're running into is that you need some implementation of an interface for testing purposes, but your production bean is defined in another module. This is what mocking frameworks like EasyMock and Mockito are for; they allow you to write simple placeholder implementations so that you can test TicketCore specifically, and it's a good idea to use them even when the real implementation is available so that you can be sure you're just testing one component at a time.

In TicketTest, you're correctly defining your Mockito mock for ITicketDAO, but your TicketCore is still trying to autoacquire the bean from Spring, even though you haven't registered it. Either manually register your bean into the test context, or place the definition for the mock in an @Configuration in src/test.

Upvotes: 3

Related Questions