It is now time to transform the test cases into automated JUnit tests. Writing those tests is mostly a mechanical task. The creative part is coming up with inputs to exercise the specific partition and understanding the correct program output for that partition.
The automated test suite is shown in listings 2.3 through 2.7. They are long but easy to understand. Each call to the substringsBetween
method is one of our test cases. The 21 calls to it are spread over the test methods, each matching the test cases we devised earlier.
First are the tests related to the string being null or empty.
Listing 2.3 Tests for substringsBetween
, part 1
import org.junit.jupiter.api.Test; import static ch2.StringUtils.substringsBetween; import static org.assertj.core.api.Assertions.assertThat; public class StringUtilsTest { @Test void strIsNullOrEmpty() { assertThat(substringsBetween(null, "a", "b")) ❶ .isEqualTo(null); assertThat(substringsBetween("", "a", "b")) ❷ .isEqualTo(new String[]{}); } }
❶ This first call to substringsBetween is our test T1.
Next are all the tests related to open
or close
being null or empty.
Listing 2.4 Tests for substringsBetween
, part 2
@Test
void openIsNullOrEmpty() {
assertThat(substringsBetween("abc", null, "b")).isEqualTo(null);
assertThat(substringsBetween("abc", "", "b")).isEqualTo(null);
}
@Test
void closeIsNullOrEmpty() {
assertThat(substringsBetween("abc", "a", null)).isEqualTo(null);
assertThat(substringsBetween("abc", "a", "")).isEqualTo(null);
}
Now come all the tests related to string and open
and close
tags with length 1.
Listing 2.5 Tests for substringsBetween
, part 3
@Test
void strOfLength1() {
assertThat(substringsBetween("a", "a", "b")).isEqualTo(null);
assertThat(substringsBetween("a", "b", "a")).isEqualTo(null);
assertThat(substringsBetween("a", "b", "b")).isEqualTo(null);
assertThat(substringsBetween("a", "a", "a")).isEqualTo(null);
}
@Test
void openAndCloseOfLength1() {
assertThat(substringsBetween("abc", "x", "y")).isEqualTo(null);
assertThat(substringsBetween("abc", "a", "y")).isEqualTo(null);
assertThat(substringsBetween("abc", "x", "c")).isEqualTo(null);
assertThat(substringsBetween("abc", "a", "c"))
.isEqualTo(new String[] {"b"});
assertThat(substringsBetween("abcabc", "a", "c"))
.isEqualTo(new String[] {"b", "b"});
}
Then we have the tests for the open
and close
tags of varying sizes.
Listing 2.6 Tests for substringsBetween
, part 4
@Test
void openAndCloseTagsOfDifferentSizes() {
assertThat(substringsBetween("aabcc", "xx", "yy")).isEqualTo(null);
assertThat(substringsBetween("aabcc", "aa", "yy")).isEqualTo(null);
assertThat(substringsBetween("aabcc", "xx", "cc")).isEqualTo(null);
assertThat(substringsBetween("aabbcc", "aa", "cc"))
.isEqualTo(new String[] {"bb"});
assertThat(substringsBetween("aabbccaaeecc", "aa", "cc"))
.isEqualTo(new String[] {"bb", "ee"});
}
Finally, here is the test for when there is no substring between the open
and close
tags.
Listing 2.7 Tests for substringsBetween
, part 5
@Test
void noSubstringBetweenOpenAndCloseTags() {
assertThat(substringsBetween("aabb", "aa", "bb"))
.isEqualTo(new String[] {""});
}
}
I decided to group the assertions in five different methods. They almost match my groups when engineering the test cases in step 5. The only difference is that I broke the exceptional cases into three test methods: strIsNullOrEmpty
, openIsNullOrEmpty
, and closeIsNullOrEmpty
.
Some developers would vouch for a single method per test case, which would mean 21 test methods, each containing one method call and one assertion. The advantage would be that the test method’s name would clearly describe the test case. JUnit also offers the ParameterizedTest
feature which could be used in this case.
I prefer simple test methods that focus on one test case, especially when implementing complex business rules in enterprise systems. But in this case, there are lots of inputs to test, and many of them are variants of a larger partition, so it made more sense to me to code the way I did.
Deciding whether to put all tests in a single method or in multiple methods is highly subjective. We discuss test code quality and how to write tests that are easy to understand and debug.
Also note that sometimes there are values we do not care about. For example, consider test case 1: str
is null. We do not care about the values we pass to the open
and close
tags here. My usual approach is to select reasonable values for the inputs I do not care about—that is, values that will not interfere with the test.
Leave a Reply