Unit Testing

apple_pieI will provide additional details on this question towards the end of this blog entry. That said; write a piece of code that would perform the following task:

Using a counter from 0 to 50 print the word “Apple” when the counter is divisible by 3. Print the word “Pie” when the counter is divisible by 7. Print the words “Apple Pie” when the counter is divisible by 3 and 7.

With the information provided, not looking for more information, I would come with the following solution (code written in Java):

public class Solution {

public static void main(String[] args) {

for (int i = 0; i < 50; i++) {

if ((i % 3) == 0) {

System.out.println(“<<< i: ” + i + ” Apple”);

}

if ((i % 7) == 0) {

System.out.println(“<<< i: ” + i + ” Pie”);

}

if (((i % 3) == 0) && ((i % 7) == 0)) {

System.out.println(“<<< i: ” + i + ” Apple Pie”);

}

}

}

}

The code produces the following output:

<<< i: 0 Apple

<<< i: 0 Pie

<<< i: 0 Apple Pie

<<< i: 3 Apple

<<< i: 6 Apple

<<< i: 7 Pie

<<< i: 9 Apple

<<< i: 12 Apple

<<< i: 14 Pie

<<< i: 15 Apple

<<< i: 18 Apple

<<< i: 21 Apple

<<< i: 21 Pie

<<< i: 21 Apple Pie

<<< i: 24 Apple

<<< i: 27 Apple

<<< i: 28 Pie

<<< i: 30 Apple

<<< i: 33 Apple

<<< i: 35 Pie

<<< i: 36 Apple

<<< i: 39 Apple

<<< i: 42 Apple

<<< i: 42 Pie

<<< i: 42 Apple Pie

<<< i: 45 Apple

<<< i: 48 Apple

<<< i: 49 Pie

I introduced the artifact (to be removed from the output) to display the counter to test the output. Let’s assume the code generated the proper output (which in this case it did). The real question:  how would you test the code?effective-unit-testing-for-java-developers

One could inject some code to test in the loop. That is typically not the best approach because it would slow down the actual application (hard to associate with this simple example). The second option would be to refactor the code and provide unit tests for each method.

Let’s first take a look at the following output and compare it to the original one:

<<< i: 0 Apple Pie

<<< i: 3 Apple

<<< i: 6 Apple

<<< i: 7 Pie

<<< i: 9 Apple

<<< i: 12 Apple

<<< i: 14 Pie

<<< i: 15 Apple

<<< i: 18 Apple

<<< i: 21 Apple Pie

<<< i: 24 Apple

<<< i: 27 Apple

<<< i: 28 Pie

<<< i: 30 Apple

<<< i: 33 Apple

<<< i: 35 Pie

<<< i: 36 Apple

<<< i: 39 Apple

<<< i: 42 Apple Pie

<<< i: 45 Apple

<<< i: 48 Apple

<<< i: 49 Pie

The updated application now only displays “Apple Pie” when the third condition is encountered. Such enhancement is typical when developing software. Yes I know; I have not removed the counter from the output yet.

The code looks as follows:

package apple.pie;

public class Solution {

public static void main(String[] args) throws Exception {

ApplePie applePie = new ApplePie();

for (int i = applePie.begin; i < applePie.end; i++) {

applePie.printString(i);

}

}

}

It is making use of a new ApplePie class. The printString() method handles the printing of the proper string. The ApplePie class looks as follows:

package apple.pie;

public class ApplePie {

// **** constants ****

private final String firstString  = “Apple”;

private final String secondString = “Pie”;

private final int firstMultiple   = 3;

private final int secondMultiple = 7;

public final int begin                   = 0;

public final int end              = 50;

// **** constructor ****

public ApplePie() {}

// **** methods ****

public void printString(int i) throws Exception {

if ((i < begin) || (i > end)) {

throw new Exception(“<<< EXCEPTION i out of bound”);

}

if (((i % firstMultiple) == 0) && ((i % secondMultiple) == 0)) {

printBothStrs(i);

} else if ((i % firstMultiple) == 0) {

printFirstStr(i);

} else if ((i % secondMultiple) == 0) {

printSecondStr(i);

}

}

public void printFirstStr(int i) throws Exception {

if ((i < begin) || (i > end)) {

throw new Exception(“<<< EXCEPTION i out of bound”);

}

if ((i % firstMultiple) == 0) {

System.out.println(“<<< i: ” + i + ” ” + firstString);

} else {

throw new Exception(“<<< EXCEPTION i not first multiple”);

}

}

public void printSecondStr(int i) throws Exception {

if ((i < begin) || (i > end)) {

throw new Exception(“<<< EXCEPTION i out of bound”);

}

if ((i % secondMultiple) == 0) {

System.out.println(“<<< i: ” + i + ” ” + secondString);

} else {

throw new Exception(“<<< EXCEPTION i: ” + i + ” not second multiple”);

}

}

public void printBothStrs(int i) throws Exception {

if ((i < begin) || (i > end)) {

throw new Exception(“<<< EXCEPTION i out of bound”);

}

if (((i % firstMultiple) == 0) && ((i % secondMultiple) == 0)) {

System.out.println(“<<< i: ” + i + ” ” + firstString + ” ” + secondString);

} else {

throw new Exception(“<<< EXCEPTION i not first and second multiple”);

}

}

}

hourglass-iconWe have some constants, a constructor and most important the printString() method contains 3 methods which can be individually tested. That should address most (I spent about one and a half hours in the new version of the code) unit testing concerns. But wait, there is more. The following code implements some unit tests. More could be
added, but at this point with such a simple example, I believe this is enough. The code for the unit testing follows:

package apple.pie;

public class UnitTest {

public static void main(String[] args) {

ApplePie applePie    = new ApplePie();

int i                      = 1;

for (i = (applePie.begin – 1); i < (applePie.end + 1); i++) {

try {

applePie.printFirstStr(i);

} catch (Exception e) {

System.out.println(“<<< EXCEPTION applePie.printFirstStr i: ” + i);

e.printStackTrace();

}

try {

applePie.printSecondStr(i);

} catch (Exception e) {

System.out.println(“<<< EXCEPTION applePie.printSecondStr i: ” + i);

e.printStackTrace();

}

try {

applePie.printBothStrs(i);

} catch (Exception e) {

System.out.println(“<<< EXCEPTION applePie.printBothStrs i: ” + i);

e.printStackTrace();

}

}

}

}

The output for a pass follows. Please note that in general, exceptions in a unit test should be limited so the output is small and calls the attention of the person conducting the test. Data flowing is never (should have said seldom) a good idea.

<<< EXCEPTION applePie.printFirstStr i: -1

<<< EXCEPTION applePie.printSecondStr i: -1

java.lang.Exception: <<< EXCEPTION i out of bound

at apple.pie.ApplePie.printFirstStr(ApplePie.java:40)

at apple.pie.UnitTest.main(UnitTest.java:12)

java.lang.Exception: <<< EXCEPTION i out of bound

at apple.pie.ApplePie.printSecondStr(ApplePie.java:53)

at apple.pie.UnitTest.main(UnitTest.java:19)

java.lang.Exception: <<< EXCEPTION i out of bound

at apple.pie.ApplePie.printBothStrs(ApplePie.java:67)

at apple.pie.UnitTest.main(UnitTest.java:26)

<<< EXCEPTION applePie.printBothStrs i: -1

<<< i: 0 Apple

<<< i: 0 Pie

<<< i: 0 Apple Pie

<<< EXCEPTION applePie.printFirstStr i: 1

java.lang.Exception: <<< EXCEPTION i not first multiple

at apple.pie.ApplePie.printFirstStr(ApplePie.java:46)

at apple.pie.UnitTest.main(UnitTest.java:12)

java.lang.Exception: <<< EXCEPTION i: 1 not second multiple

<<< EXCEPTION applePie.printSecondStr i: 1

at apple.pie.ApplePie.printSecondStr(ApplePie.java:59)

at apple.pie.UnitTest.main(UnitTest.java:19)

java.lang.Exception: <<< EXCEPTION i not first and second multiple

<<< EXCEPTION applePie.printBothStrs i: 1

at apple.pie.ApplePie.printBothStrs(ApplePie.java:73)

at apple.pie.UnitTest.main(UnitTest.java:26)

java.lang.Exception: <<< EXCEPTION i not first multiple

:::: ::::

<<< EXCEPTION applePie.printFirstStr i: 49

java.lang.Exception: <<< EXCEPTION i not first multiple

at apple.pie.ApplePie.printFirstStr(ApplePie.java:46)

at apple.pie.UnitTest.main(UnitTest.java:12)

<<< i: 49 Pie

<<< EXCEPTION applePie.printBothStrs i: 49

java.lang.Exception: <<< EXCEPTION i not first and second multiple

at apple.pie.ApplePie.printBothStrs(ApplePie.java:73)

at apple.pie.UnitTest.main(UnitTest.java:26)

<<< EXCEPTION applePie.printFirstStr i: 50

java.lang.Exception: <<< EXCEPTION i not first multiple

at apple.pie.ApplePie.printFirstStr(ApplePie.java:46)

at apple.pie.UnitTest.main(UnitTest.java:12)

<<< EXCEPTION applePie.printSecondStr i: 50

java.lang.Exception: <<< EXCEPTION i: 50 not second multiple

at apple.pie.ApplePie.printSecondStr(ApplePie.java:59)

at apple.pie.UnitTest.main(UnitTest.java:19)

<<< EXCEPTION applePie.printBothStrs i: 50

java.lang.Exception: <<< EXCEPTION i not first and second multiple

at apple.pie.ApplePie.printBothStrs(ApplePie.java:73)

at apple.pie.UnitTest.main(UnitTest.java:26)

For convenience, most of the output was removed.

So there you have it, something that started quite simple as writing a loop with 3 conditions, evolved into something somewhat more complex. This is a reminder to always spend the upfront time collecting all the requirements. Also, as the development progresses, cycle from architecture to development in order to refine requirements.

If you have comments or questions I would like to know about them.

John

john.canessa@gmail.com

 

 

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *