A simple lesson in coding: testing first!

Leo Irakliotis
CodeX
Published in
4 min readAug 19, 2021
Alexandre Arrechea’s Orange Tree, on display at the 21C Hotel in Bentonville, Ark. (Photo by the author, 2021).

This is the third article in a series of four, about how a simple idea evolved into a course plan for students taking their first class in data structures. The first article described the simple example that we’ll keep using here: a method to tell if a String is a palindrome or not. The second article refined the method and the result is shown below.

The code looks nice but as we are about to discover, it is still incomplete. It works with mixed-case arguments because .toLowerCase forces everything to lower case first. The code removes spaces with .replace, recognizing arguments like “Race Car” as palindromes. It even removes punctuation and numbers with .replaceAll, to process palindromes like “a man, a plan, a canal: Panama”. (Yes, we can combine replace and replaceAll into a single replaceAll. For illustrative purposes, I am leaving them separated).

If we were to write a few simple tests, we might even get impressed by the accuracy of the code:

Simple testing for method isPalindrome() hosted in a class named Palindrome.

So, it works, right? It depends on how we feel when the method tells us that these strings are palindromes:"5A", "...", "101", "123", "..-", and "" (the empty string).

Strings "..." and "101" could be considered palindromes. They are the same sequence of characters going forward and backward. But that not how our method determines them to be palindromes. Line 4, of our method, removes all non-letter characters from these strings. Both "..." and "101" are reduced to the empty string. The length of the empty string is 0. The while loop in line 6 will not run (because the condition pos < s.lengthis false). And the method returns the initial value of palindrome, which was set to true.

Basically, the method returns true for any string that has non-letter characters, e.g., "@123.456", "3.14", etc. And strings with only one letter like "555A" will be reduced to "a" after they’re washed by the replaceAll() and toLowerCase() methods of line 4. Our method will recognize "555A" as a palindrome.

Finally, imagine the following code:

String s = null;
isPalindrome(s);

This will cause a null pointer exception, a runtime error that we’d like to avoid.

The earlier exuberance is now deflating. Our method is not as cool and awesome as we thought. It works with palindromes like “a man, a plan, a canal: Panama”, but it also produces erroneous results. That’s because I rushed to write code, feeling over-confident about the implementation. I did not take time to internalize the requirements for the method nor to plan how to test it. Let’s fix this now.

First, let’s describe what we want the method to do: return true is a non-empty string is a palindrome. Palindromes comprise letters only. Spaces, letter case, and punctuation marks do not affect palindromic properties. Strings like “Race car!” are palindromes. String like “3.14PIIP41.3”, are not.

Next, let’s contemplate some more testing. In addition to the assertTrue calls shown earlier, we want our method to pass the following tests as well.

To meet these expectations, we must go to the drawing table. We should have gone there earlier, but we had to learn our lesson first. The pseudocode we need, is shown below and it includes all three major conditions we discussed so far: check for null values, check for valid content, and check for empty strings.

boolean isPalindrome(String s)
if s is not null
if s has valid content
remove spaces and punctuation from s
if s not empty after removal of spaces and punctuation
determine palindromic property

It took us a while but we are finally talking about what kind of strings we want to evaluate. We want only letters, spaces, and punctuation marks. Therefore, we need a helper method first to determine if our string is a valid input, i.e, contains only allowed characters.

Writing a helper method seems easy. Take a string character-by-character and the moment we find a non-allowed value, declare it unfit for our palindrome method. But there may be a catch. Such a helper method may disqualify strings like "555A" because numbers are not allowed. Strings like ".!!." will be validated because they contain punctuation marks. Are we ok with that? Do we need to have at least one letter present? Let’s say we do. A string must have at least one letter and cannot contain anything but letters, spaces, and punctuation marks. Method isValid, below, seems to meet this specification.

The method above returns the AND of two variables: hasValidCharacters and atLeastOneLetterPresent. The first variable is true when every character in the string is valid, i.e., a letter or a punctuation mark or a space. The second variable is true when at least one of the string characters is a letter. Valid characters are assessed at line 11. This line checks if a character is between a and z, or if the character is present in a sequence of allowed characters. The presence of at least one letter is assessed in line 12. Once the condition 'a' <= c && c <= 'z' evaluates to true, it sets variable atLeastOneLetterPresent to true no matter what comes next.

With the help of isValid, we can now finish the palindrome method, as shown below.

Next: A simple lesson in coding: the playground.

--

--

Leo Irakliotis
CodeX
Writer for

Chicago-based educator and technologist, passionate about data, innovative learning, coding, flying, diving, espresso, dogs, REV, photography, Door County.