Unit test are important part of every application. Even though we sometimes hate to write time and think they are just time consuming, they can make our app must more stable and bulletproof. One often scenario that we want to test is to check if some method in a class was called with certain parameters. We have a class Importer to read and import a record from XML.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Importer { public void readXML(File file) { // read record from XML // when everything successfully read, call process ... this.process(record); } public void process(Record record) { // save record in database } } |
We want to test if XML was correctly read and if our process(record) method was called with record object.
Solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
@RunWith(MockitoJUnitRunner.class) public class ImporterTest { public void shouldSucceed_When_ImportValidXMLRecord() { // prepare File file = new File("record.xml"); Importer importer = Mockito.spy(new Importer()); // spy enables us to mock actual classes // run importer.readXML(file); // results ArgumentCaptor<Record> captur = ArgumentCaptor.for(Record.class); verify(importer).process(captur.capture()); assertThat(captur.getValue().firstName).isEqualTo("Bob"); // XML contains attribute with firstName = Bob } } |
ArgumentCaptur enables us to capture actual object that is passed to our process(record) method. We don’t only test if method was called, but also if it was called with specific values.
Extra tip for collections
Again, let’s image we modify our method to be able to read multiple records.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class ImporterBatch { public void readXML(File file) { // read all records from XML // when everything successfully read, call process ... this.process(records); } public void process(Set<Record> records) { // save all records in database } } |
Solution is similar. To test collection (List, Set, Map…) arguments, we have to use some additional Mockito features.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@RunWith(MockitoJUnitRunner.class) public class ImporterBatchTest { // it will automatically create a new instance @Captor private ArgumentCaptor<Set<Record>> recordsCaptor; public void shouldSucceed_When_ImportValidBatchXMLRecords() { // prepare File file = new File("records.xml"); ImporterBatch importer = Mockito.spy(new ImporterBatch()); // run importer.readXML(file); // results verify(importer).process(recordsCaptor.capture()); assertThat(recordsCaptor.getValue().size).isEqualTo(2"); // our XML contains 2 records } } |
In our test, recordsCaptor.getValue() returns Set<Record>. We can check size or each record. So with Mockito you can test a lot of scenarios and it’s a really nice tool for unit testing.