Writing code that uses external APIs is not trivial and testing it neither. This post describes how to drive code around external APIs initially and is the second part of How to drive code by sketching it. The test-approach is only one of a couple of possibilities and used just to outline how to drive code via Quick-Fix of an IDE like eclipse.
Walk through the former post and prepare the code at the end of it in your IDE if not happened yet.
Let’s assume we know the external Printer-API a bit. E.g. the API provides something like a print-job scheduler. I.e. our ExtPrinter has to deal with this and to ensure the text has been printed, we need to check in a test whether a job has been triggered with our payload or not. At this point we do not care about the scheduling aspect, we assume that the text is printed immediately.
Currently the code-snippet of the last post looks like:
@Test public void test() { Printer myPrinter = new Printer(new AnExtPrinter()); myPrinter.print("a text"); }
First Assert
To be prepared for the first Assert we assign AnExtPrinter to a variable. In the code shown above hit CTRL + 1 on the new of new AnExtPrinter() and select extract local variable and replace …. A new local variable anExtPrinter has been created. Now we want to check the printed text just by calling a method like getPrinteText(). Let’s insert the first Assert statement and type what we want:
@Test public void test() { AnExtPrinter anExtPrinter = new AnExtPrinter(); Printer myPrinter = new Printer(anExtPrinter); myPrinter.print("a text"); Assert.assertEquals("a text",anExtPrinter.getPrintedText()); }
The compiler will complain on the missing Assert class first. So just import it, try CTRL + 1. And of course the method getPrintedText() is missing. Hit CTRL + 1 again on the red underlined part, let the method be created and choose the return-type String. It will look like:
public class AnExtPrinter implements ExtPrinter { public String getPrintedText() { // TODO Auto-generated method stub return null; } }
The Assert will fail until we implement the first logic, because we compare the String „a text“ with null. To implement the logic, which does the call on the external API, let’s further drive the code via CTRL + 1. To use anExtPrinter, provided in the constructor, and to call a method on it, we need a member variable, which can be used in the print method of our new class Printer. So, mark the parameter anExtPrinter in the constructor and hit CTRL + 1. The IDE suggests Assign parameter to new field anExtPrinter, exactly what we need. It looks like:
public class Printer { private ExtPrinter anExtPrinter; public Printer(ExtPrinter anExtPrinter) { this.anExtPrinter = anExtPrinter; // TODO Auto-generated constructor stub } public void print(String text) { } }
Now we want to call a print method on anExtPrinter, which obviously does not exist yet. So let’s write the method call as we want it like anExtPrinter.print(text). We don’t want to write the new method by our self. Let’s just mark the red underlined code and hit CTRL + 1 to create the method in the class AnExtPrinter.
public class Printer { private ExtPrinter anExtPrinter; public Printer(ExtPrinter anExtPrinter) { this.anExtPrinter = anExtPrinter; // TODO Auto-generated constructor stub } public void print(String text) { anExtPrinter.print(text); } }
But the method is created in the interface ExtPrinter, because we are working with the interface and not the implementing class AnExtPrinter itself. So the compiler complains about the missing method in the implementation AnExtPrinter.
First Mock
Let’s recap for a second. We have got one method getPrintedText() created in AnExtPrinter (just for test purposes) and the method print(String text) in the interface ExtPrinter. So the part of the code needed for the test and the part needed in the production code has been distinguished and separated automatically. Our class AnExtPrinter drives into the direction of a Mock – more details about Mocks follow.
Mark the red part and hit CTRL + 1 and the item Add unimplemented methods creates the method to be implemented.
The class AnExtPrinter now looks like:
public class AnExtPrinter implements ExtPrinter { public String getPrintedText() { // TODO Auto-generated method stub return null; } @Override public void print(String text) { // TODO Auto-generated method stub } }
What is the easiest way to return the text to be printed via the method getPrintedText() actually. Even if it looks stupid, but it is just setting a member-variable of AnExtPrinter to the value, which is given in the parameter text during the call of print(String text). How to achieve that? Let’s write what we want by typing in the method print(String text) just the assignment to a new member variable printedText = text;, like:
@Override public void print(String text) { printedText = text; }
and let CTRL + 1 create the variable by choosing Create new field. The Assert will still fail until we return the new member printedText in getPrintedText() of course. Do that and it looks like:
public class AnExtPrinter implements ExtPrinter { private String printedText; public String getPrintedText() { return printedText; } @Override public void print(String text) { printedText = text; } }
The complete sketchpad will look like:
public class SketchTest { public class AnExtPrinter implements ExtPrinter { private String printedText; public String getPrintedText() { // TODO Auto-generated method stub return printedText; } @Override public void print(String text) { printedText = text; } } public class Printer { private ExtPrinter anExtPrinter; public Printer(ExtPrinter anExtPrinter) { this.anExtPrinter = anExtPrinter; // TODO Auto-generated constructor stub } public void print(String text) { anExtPrinter.print(text); } } @Test public void test() { AnExtPrinter anExtPrinter = new AnExtPrinter(); Printer myPrinter = new Printer(anExtPrinter); myPrinter.print("a text"); Assert.assertEquals("a text",anExtPrinter.getPrintedText()); } }
The interface:
public interface ExtPrinter { void print(String text); }
(Please find the source of the examples in github)
Of course a lot of code, just to check a call to an external system. But with quite less typing. However, even the external API hasn’t been connected yet in real. We only verified via our Mock AnExtPrinter that our printer will call a print method on a class that implements our new interface.
This interface could be implemented by an adapter which connects to the real Printer-API later. As a task move the string „a Text“ into a constant by using CTRL + 1. Finally our production class is still in the sketchpad and more or less not available from outside and we haven’t spend much time on meaningful naming or cleaning up code. That will follow and the classes should be moved into separate files of course, by re-factorization features of the IDE.
Pingback: Sketch and drive code | Sketch driven – software engineering