DEV Community

Cover image for How to Write Great Tests!
Phillip Ninan
Phillip Ninan

Posted on • Originally published at pninan.hashnode.dev

How to Write Great Tests!

In my previous post, I discussed a nifty sorting test. Today, I am going to teach you how to write great tests! When I write a new JUnit test I follow the same formula. This allows my tests to be written and read in a consistent manner. It also helps me write tests faster. I would like to share my format with you today!

The File

The first thing I do is create a new test file/class in my project's test package and create the test method. All my tests end with Test so it is easy to find corresponding classes. A class named SomeService should have a counterpart named SomeServiceTest.

Next, I make sure I give my test a good name. I personally prefer to omit the word test it since its redundant in a test class. The name of the test should describe what is being tested. Make you can easily differentiate similar tests.

public SomeServiceTest{
    @Test
    public void sortByPopularVoteDesc() {

    }
    @Test
    public void sortByPopularVoteAsc() {

    }
}
Enter fullscreen mode Exit fullscreen mode

Outline

Next, I put some boilerplate inside of my method. This is the same format I use for all my tests. I set up my test, I call a function, finally, I use some sort of assertion or verify to ensure the expected results.

public TestClass{
    @Test
    public void sortByPopularVote() {
      // setup
      // test
      // assert/validate
    }
}
Enter fullscreen mode Exit fullscreen mode

Let's discuss some of these components.

  • setup - This is where you should prepare your code to be tested.
  • test - This is where you should call the function being tested. This can be good to point out if you call multiple functions in your test.
  • assert/validate - This is where you actually use an assert or validate function to ensure the actual output matches what you expect.

Setup The Test

Finally, I simply fill in the blanks. If my setup is similar to all other tests I abstract this logic to a @Before function. This function gets invoked before each test. If you have a common setup between several tests, but not all of them, it is also a common practice to write a private function to encapsulate specific setup instructions. Either option makes the setup for common tests more reusable. If you have to make a change in setting up your tests you only have to make it in one place. This is very useful when a refactor breaks a certain set of tests.

public TestClass{
  private List<SortableObj> expected;
  private final SortableObj first = new SortableObj();
  private final SortableObj second = new SortableObj();
  private final SortableObj third = new SortableObj();
  private final SortableObj fourth = new SortableObj();

   @Before
   public void before{
     // some decoration on objects
     // ...
     expected = Arrays.asList(first, second, third, fourth);
   }

    @Test
    public void sortByPopularVote() {
      // setup
      List<SortableObj> actual = Arrays.asList(fourth, third, first, second);
      // test
      Collections.sort(actual);
      // assert/validate
      assertThat(actual).isEqualTo(expected);
    }
}
Enter fullscreen mode Exit fullscreen mode

Testing Nomenclature

Please note the use of the variable names expected and actual. These are keywords commonly associated with writing tests and make them more easily readable. You want the actual object returned from a function to match the expected value.

  • expected - This is the name of the variable for what you want your output to look like. In this example, we want a list to be in a certain order.
  • actual - This is the name of the variable to use for the output of the function under test. It is the "actual" output to the function.

Nomenclature - The devising or choosing of names for things, especially in a science or other discipline. In the context of software, this deals with the consistent naming of API endpoints, responses, variables, functions, and documentation.

Conclusion

Using this format you should be able to write more consistent tests in an efficient manner. This allows you to implement more maintainable code. I hope you enjoyed this post! Check out my series on JUnit tests!

Follow me on Twitter if you would like to keep up to date with my latest software content!

Discussion (0)