top of page

Better Test Code Principles: #1 Don't copy/paste the code. Reuse it.

When starting to learn Java, one of the first things you are taught is that a class consists of several things, among which are the 'methods'.  A method is nothing more than grouping of several code lines. Since tests are code, the same principle applies to writing your Java based tests. Especially if you are dealing with duplicate code (code you keep copy/pasting all across your test project).

So what would be the reasons for not wanting to duplicate your code, but instead grouping it into methods:

  1. Obviously a smaller number of lines of code will be obtained. Writing a lot of identical code is not a measure of good programming, so instead, whenever you find yourself doing Copy/Paste, consider grouping the repeating code into methods. This will reduce the number of code lines your tests consist of considerably.

  2. Single point of change: whenever parts of the repeating code need to be changed, by using methods you will only change the required code inside the containing method, and all the entities that use the method will get the updates seamlessly.  Sure, you could use the 'search and replace' functionality of your IDE to replace all the repeating code that needs to be changed at once, but this again is not a nice approach.

  3. Reading tests that contain duplicate code takes lots more time and is less fun for sure.  So is maintaining them.

Things to consider when writing methods

  1. Proper naming of methods. Whenever you extract duplicate code into a method, you need to name the method so that it properly reflects the code that it will contain. It needs to be as specific as possible. Don't forget to use camel case, to make it easy to understand and relevant to the code it holds.

  2. How many parameters will the method take? Well ideally not more than 5. Having a method that takes 20 parameters is a sign that you should reconsider the way you grouped the code. It might be that you should group the code in more than one method - this can happen when the code performs several larger 'steps', in which case you could generate several methods, based on the functionality that the code does. For example, if the code contains some steps for filling in a very large form by providing Strings as input for each field, you might create separate methods for each of the larger sections of the form. This would be helpful, since maybe for some tests, you will only want to fill in parts of the form.

  3. Don't pass in as a method parameter a value that within the method is actually a constant. That is: if for one of the parameters you always pass in the exact same value, instead of having it as a parameter, you could simply define the value for that parameter as a constant inside the method. This way you can reduce the number of parameters and avoid passing the same value each time.

  4. Group the repeating code based on functionality. If the number lines of code that are repeating is very high, you can, instead of one method, define several methods. These should break the lines of code into the functionality they serve.

  5. Short method calls: let's say that one of the parameters is a bit longer to write. Like, for example - you want to pass the String value to lowerCase of the first element of a list. So instead of passing the value of the parameter similarly to this: list.get(firstElement).toString().toLowerCase, you could just pass in the list (if you are going to use other of its' elements inside the rest of the method anyway), or the first element. Then, just leave the toLowerCase processing to be done inside the method.

  6. Where to place the method is another aspect to consider. If the code found within the method could or should be used in other test classes also, instead of writing the same method in each class (hence yet again duplicating code), you could write it in a class from which all the calling code will have access to it (call it a utility or helper class). Or, if it makes sense, move all other test methods that call the helper method into the same class where it was initially declared.

When you are trying to extract methods from duplicate code you could either manually create the method, move the code inside it and replace the initial code with a call to the new method. Or, if you are using IntelliJ, you can do this automatically. Below you can see how.

Example

For the purpose of an example, i created a test class that contains 7 test methods. Each of these methods have identical first 5 lines of code and a 6-th line of code that only differs by a value. To make the example even easier, all the lines are just plain system.outs, so they just output something to the console.

What the code looks like, in the first test method:

@Test
public void firstTest() {
    System.out.println("I want to show an example");
    System.out.println("Of how many test code lines");
    System.out.println("Can be improved");
    System.out.println("By using an approach");
    System.out.println("Based on methods");
    System.out.println("This is the code from method 1");
} 

So the only difference in code from the first test method to the others is the last line of code from each method, where a number appears, corresponding to the name of the method (first method has 1, second method has 2, and so on).

So the 7th method for example has the following last line of code:

System.out.println("This is the code from method 7"); 

In this case, the total number of lines of code is 76, consisting of: 7 test methods, class definition, imports.

In order to reduce the number of code lines, i will use the IntelliJ refactoring feature, that will allow me to group identical lines of code into methods. (Note: i am using the free version of IntelliJ so the screens might be a bit different in the Ultimate edition.)

First, i will look at the whole first test method. Since i already know that all the code is identical except for one value found on the last line of the code, i will select all of the test code lines.

Then, i will hit the following shortcut on the keyboard: Ctrl + Alt + M. A new window will be displayed, as in the screenshot below:

In this screen you will define the new method in which you want to hold your repeating code.

The first thing to consider is where you will store this new method and what kind of visibility modifier you want to add to it.  If you store it in the same class in which you are currently working, make it private. If you would like to move the repeating code into a class that you will extend, you should make it protected.  Public is not usually recommended, but if you want to choose it, go ahead. The visibility modifier is set from the drop down from the top left side of the window.

Next, you need to define the name for the method you will create. For this example i will go for 'printMethod'.  The screen with all these preferences set looks like this:

In my current editor i am not able to also add a new parameter to the new method's signature, so i will just click the 'OK' button.

In the new screen that appears, the editor noticed that except for the last line, the other lines that i separated into the method are identical. It suggests that i should add a new String parameter to my method, in order to pass in the differing String to the last system.out that i had in my tests.

I will accept the suggestion, and so i will get yet a new screen asking me for the first encounter of the duplicate code - if i want to change it to the new method call.

I will choose 'All' so that i don't get asked the same question for each of the duplicate code occurrence.

At this time, the whole duplicate code is removed and changed with the call to the new method, passing in the varying String as parameter (the one used for the last system.out of the each test).

One more adjustment i will normally make is moving the newly generated method from where it was created (at the beginning of the class) to the lowest part of the class (i prefer to have tests first, test data below).

The result is a decrease of code lines from 76 to 50 with this simple refactoring, short tests, and one single point of change in case any of the previously repeating lines of code need changing.

Recommended further reading

  1. https://docs.oracle.com/javase/tutorial/java/javaOO/methods.html

  2. https://docs.oracle.com/javase/tutorial/java/javaOO/arguments.html

  3. https://docs.oracle.com/javase/tutorial/java/javaOO/returnvalue.html

Recent Posts

See All

Creating an Architecture for Your Automated Tests Writing Automated Tests in Small Increments 4 Times Your Automation Passed While Bugs Were Present Now That You’ve Created Automated Tests, Run Them!

This week my newest article was released, this time on how to use Log4j to log relevant test automation information: https://blog.testproject.io/2021/05/05/logging-test-automation-information-with-log

bottom of page