Codementor Events

How and why I built BooleanText Class

Published May 25, 2020

About me

I am Yehoshua Kahan, and in January 2019 I was hired to build scripts for automated testing of online scholarly tools.

The problem I wanted to solve

Each test had three steps.

  1. Setup. In this step, I would implement one or more--sometimes many more--manipulations of the tool under test, imitating the actions an end-user might take as he carried out some task.
  2. Test. Having manipulated the tool in accordance with the user story under consideration, I would then capture the tool's output and run an assertion test. This assertion test, of the form assert(boolean, String), would have the effect of doing nothing if the output was as anticipated, but throw an error if the output was not as anticipated.
  3. Cleanup. Here, I would reverse all of the setup manipulations, restoring the tool to a pristine state for the next test.

Often, a testing suite would consist of many discrete tests; in some cases, close to a hundred tests.

However, whenever the assertion test failed--that is, when the boolean argument was false rather than true--all the remaining code in that particular test would fail to run. This means that cleanup would not occur, and this would often prevent all or many of the remaining tests in the suite from running. Since the remaining tests assume that they are encountering a "fresh" webpage, the page as it appears to the user immediately upon navigating there, a modal or an open menu item could be all it took to cause catastrophic failure across the entire remainder of the suite.

What is BooleanText Class?

I realized that my error was in step two, testing. I was combining two separate issues (record output and run an assertion test) into one step. This violation of the principle of Separation of Concerns was causing me immense annoyance and the waste of a great deal of time.

I ultimately solved this by introducing a specialty class, which I call BooleanText. A BooleanText is a fairly simple data structure, consisting of two fields and a number of simple methods.

The two fields are:

  1. A boolean.
  2. A String

In addition, it has getter and setter methods for each of the fields, and a setter method that accepts both a boolean and a String and creates the BooleanText.

I considered implementing only a creation-time setter that would take both the boolean and the String, thus forbidding any manipulation of either after the BooleanText was created. However, I realized that more complex tests might want to test several conditions, and it would be convenient to allow the user (myself) to build several intermediate BooleanTexts on the way to a final BooleanText, with the final BooleanText carrying a "false" boolean if any of the intermediate BooleanTexts did so, and a String message that pinpointed the exact failure. To allow for this, I needed to implement setters that could change the boolean and the String after creation-time.

Using a BooleanText, I could capture the output of my test, cleanup for the next test, and only then run the assertion test and display a String output if the test had failed. If the test passed, lovely. If not, there was no further code to run before the next test in the suite, and there would be no chain of catastrophic failure. Much better.

Tech stack

Since I was working in Java, it was only natural to build my new class in that same language. Nothing fancy here; just plain-vanilla Java.

The process of building BooleanText Class

Really, once the BooleanText was fully conceptualized, the process of building it was quite straightforward.

I knew I needed to declare a class; the class needed a private initialization method, a public initialization methods, two getters, and three setters, and a response method, which I will discuss below.

The common use of a BooleanText would involve testing one expectation. This test would generate a boolean value of "true" or "false" and a String output to be printed if the boolean should be "false." Hence, a two-part setter that would take advantage of a private initialization method to establish the BooleanText and to define both fields.

Less commonly, I might need to occasionally manipulate the boolean and/or the String output through several stages of testing. For this case, I wanted a public initialization method that would declare but not define the boolean and the String output, along with a setter for each of the boolean and the String.

I would need a getter for each of the boolean and the String, allowing the user (again, myself) to examine the field values as needed.

Finally, I would need a response method. This method would simply apply an assert to both the boolean and the String field that had been earlier declared and defined.

Challenges I faced

At first, I tried to solve my problem by using an Object array with a capacity of 2 objects. Since EVERYTHING in Java inherits from the Object class, I could store both a boolean and a String in an Object array, and then use them later, after cleanup.

This was a poor solution, since it required me-the-user to remember which one was the boolean and which the String. Any forgetfulness here would cause my code to throw a fit, resulting in a catastrophic failure of exactly the type I was trying to avoid.

However, once I realized that a dedicated class was the solution, and once I clearly understood the requirements, it was not hard to architect and implement it. The only thing that was even remotely tricky was the response method.

At first, I implemented this as a static method of the class which accepted the boolean and String of a particular BooleanText as arguments. Calling it looked like this: BooleanText.response(boolean, String). This worked just fine, but I eventually realized that it was a bit silly.

A better and cleaner approach was to implement it as a concrete method of the particular BooleanText, operating on the fields of that particular instance without any need for arguments. Now, a call looked like this: bt.response(). Much better.

Key learnings

The main thing I learned from this was simply that a solution is usually available, with a little thought. For several months, I looked on in frustration as one failed test triggered catastrophic failure across an entire suite. For all that time, if any random test failed, I would have to run each test individually, wasting time and annoying me no end.

The solution was there, and it was simple. All I had to do was stop and think, defining the problem, to find it.

Tips and advice

Don't get annoyed--get even! Understand the nature of the problem that you're facing, and you're going to be a good way towards solving it. And remember, programming principles like Separation of Concerns are there for a reason. They can help you pinpoint the flaw in your code, and guide you towards a solution.

Final thoughts and next steps

Sorry, I don't really have any final thoughts and next steps for this one. BooleanText solves the problem that it meant to solve, simply and elegantly. That's enough for me.

Discover and read more posts from Yehoshua Kahan
get started
post commentsBe the first to share your opinion
Show more replies