ComponentUnderTest creates the mocks in your tests for you! For a running example to see yourself clone this project and launch:
src/test/java/org/kornel/testing/it/ComponentUnderTestRuleIntegrationTest.java
Ever got tired of manually specifying mocks for your components under test? Imagine a business component:
@ComponentpublicclassBusinessProcessor {
@InjectprivateInvoiceRepositoryrepository;
@InjectprivateInvoiceValidationinvoiceValidation; publicProcessedInvoicesprocessInvoices() throwsInvoicesAlreadyProcessed {
finalInvoicesinvoices = repository.fetch();
invoiceValidation.assertUnprocessed(invoices);
returnprocess(invoices);
}
}and a test:
@RunWith(MockitoJUnitRunner.class)
publicclassBusinessProcessorTest {
@InjectMocksprivatefinalBusinessProcessorprocessor = newBussinessProcessor();
@MockprivateInvoiceRepositoryrepository;
@MockprivateInvoiceValidationinvoiceValidation; @Test(expect=InvoicesAlreadyProcessed.class)
publicvoidshouldNotProcessAlreadyProcessedInvoices() throwsInvoicesAlreadyProcessed {
// givengiven(repository.fetch()).willReturn(...)
given(invoiceValidation.assertUnprocessed(...)).willThrow(...)
// whenprocessor.processInvoices()
// then// exception
}
}Specifing each of the mocks is a too repetitive and daunting task. Imagine the test looked as follows:
publicclassBusinessProcessorTestextendsTestBase {
@ComponentUnderTestprivateBusinessProcessorprocessor;
@Test(expect=InvoicesAlreadyProcessed.class)
publicvoidshouldNotProcessAlreadyProcessedInvoices() throwsInvoicesAlreadyProcessed {
// givengiven(processor.repository.fetch()).willReturn(...)
given(processor.invoiceValidation.assertUnprocessed(...)).willThrow(...)
// whenprocessor.processInvoices()
// then// exception
}
}The dependencies are mocked automatically for you! This is accomplished by:
- A JUnit rule which scans for a ComponentUnderTest object in your tests (defined in TestBase in this examples)
- The object is then scanned for fields annotated with a specific annotation (e.g. @Inject or @Autowired)
- The fields are mocked using e.g. Mockito.mock
This approach requires the dependencies injected in your tests component to be package scope (assuming the test is in the same package as the tested component, which it should), but that does not seem as a too big sacrifice for the purpose of easier, quicker and more efficient TDD.
The package scoped dependencies allow for easier stubbing:
given(processor.repository.fetch())