Image by Brian Kenney via Shutterstock, with addition of Mockito Made Clear cover

Unboxing Day, Part III

Mockito Made Clear

Kenneth Kousen
5 min readFeb 8, 2023

--

https://pragprog.com/newsletter/

What good is an unboxing video for a Mockito book that doesn’t use Mockito?

In Part 1 of this series, I showed how to programmatically open a downloaded file. Part 2 was all about how to do the download of the file in the first place. In this third and final part, I’ll add a class to print the file, which will allow me to test the overall system by mocking it.

Printing a File in Java

The java.desktop module includes the javax.print packages, which have been available in the language since version 1.4, believe it or not. The main Javadoc page for the javax.print package gives an idea of what to do:

The Javadocs for the javax.print package

Further down on the page they explain how to use the API:

  1. Choose a DocFlavor.
  2. Create a set of attributes.
  3. Locate a print service that can handle the print request as specified by the DocFlavor and the attribute set.
  4. Create a Doc object encapsulating the DocFlavor and the actual print data, which can take many forms including: a Postscript file, a JPEG image, a URL, or plain text.
  5. Get a print job, represented by DocPrintJob, from the print service.
  6. Call the print method of the print job.

Sounds like a lot of work, but it’s not too bad. I took their provided example and reworked it a bit into the following method inside my PrinterService class:

public String printPdf(String fileName) {
try (FileInputStream fis = new FileInputStream(fileName)) {
Doc pdfDoc = new SimpleDoc(fis, DocFlavor.INPUT_STREAM.PDF, null);
PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
aset.add(MediaSizeName.ISO_A4);
aset.add(new Copies(1));
aset.add(Sides.DUPLEX);
PrintService[] services = PrintServiceLookup.lookupPrintServices(
DocFlavor.INPUT_STREAM.PDF, aset);
if (services.length > 0) {
DocPrintJob job = services[0].createPrintJob();
job.print(pdfDoc, aset);
return "Printed " + fileName;
} else {
return "No printer services available";
}
} catch (PrintException | IOException e) {
throw new RuntimeException(e);
}
}

Here’s a simple test class that doesn’t do much, other than verify the method ran at all:

class PrinterServiceTest {
private final PrinterService printerService = new PrinterService();

@Test
void printPdf() {
String result = printerService.printPdf("src/main/resources/pg1661-images-3.epub");
assertThat(result).containsAnyOf(
"Printed",
"No printer services available");
}
}

On my laptop, which does not have access to a local printer (hardware things whose primary purpose is to warn you about low ink levels on colors you never use), the result is “No printer services available,” as expected.

Mocking the PrinterService

Now that the main Unboxing class has an actual dependency, which I wrote, we can write a test for Unboxing that works with a mock PrinterService.

The first step: modify the Unboxing class to use a PrinterService:

public class Unboxing {
private PrinterService printerService = new PrinterService();

public void setPrinterService(PrinterService printerService) {
this.printerService = printerService;
}

public String sendToPrinter(String fileName) {
return printerService.printPdf(fileName);
}

// ... other methods ...
}

Normally I would make the dependency a constructor argument, or even instantiate it directly in the constructor. It’s easier to keep it as an attribute with a setter method in this case.

To test the Unboxing class, provide a mock for the the PrinterService, set the expectations on the printPdf method, and verify that it got invoked. We’ll take advantage of the limited dependency injection capabilities that come with Mockito by using the @Mock and @InjectMocks annotations. Mockito will instantiate the class under test and the dependency, and call the setter method for it automatically. To make that work, add the Mockito JUnit 5 extension to the class.

@ExtendWith(MockitoExtension.class)
class UnboxingTest {
@Mock
private PrinterService printerService;

@InjectMocks
private Unboxing unboxing;

// tests below ...
}

This approach saves you the trouble of instantiating all the relevant classes and calling the setter methods yourself. You can proceed directly to setting the expectations, checking the behavior, and verifying that the right method was called, the right number of times, with the right arguments.

@Test
void sendToPrinter() {
// Set the expectations on the printPdf method
// (Yes, it's an epub this time, but the same idea applies)
when(printerService.printPdf(argThat(s -> s.endsWith(".epub"))))
.thenReturn("""
We printed your file!
Thank you for using our service.

We also decided, for no good reason,
to scan it back into a pdf,
which you can pick up at your leisure.

Please bring a 3 1/2" floppy disk for the scanned document.
""");

// Invoke sendToPrinter on the class under test (Unboxing)
String result = unboxing.sendToPrinter("src/main/resources/pg1661-images-3.epub");

// Check the output of sendToPrinter
assertThat(result).contains("We printed your file!");

// Check that the printPdf method was called exactly once with a String arg
verify(printerService).printPdf(anyString());
}

The test relies on the static when and verify methods from the Mockito class. It also uses the static argThat method from ArgumentMatchers, and a lambda expression supplies a custom matcher. Just for fun, a Text Block from JDK 15 returns a silly message when the printPdf (or, in this case, printEpub, because the test file from Project Gutenberg is that type instead of a pdf) method is invoked. My profound apologies for subjecting you to my sense of humor.

All those capabilities (other than the Text Block, which comes from Java rather than Mockito) are covered in Mockito Made Clear so please consider checking it out.

There you have it — a complete Java system that downloads a file from a Dropbox link, opens it on the local machine, and threatens to print it — but not really. If you squint, you can see this as a moderately reasonable way to unbox an ebook. I may assemble an unboxing video and upload it, so watch out for that.

I hope you found the journey enjoyable, or at least mildly entertaining. As usual, all the source code can be found in this GitHub repository.

Cover of Mockito Made Clear by Ken Kousen featuring limes and mint as a play on “mojito”
Cover of Mockito Made Clear by Ken Kousen

--

--

Kenneth Kousen
The Pragmatic Programmers

Author of the books Mockito Made Clear, Help Your Boss Help You, Kotlin Cookbook, Modern Java Recipes, Gradle Recipes for Android, and Making Java Groovy