Have too Many Assertions in Test Scripts? Use Custom Assertions Instead




If your JUNIT test script uses too many assertions, you can reduce their number with
  • custom assertions  and 
  • test objects

Having less assertions in the test script makes the script shorter, easier to read and maintain.

These are things that need to be done on all code, both for application and tests.



Lets start with an example

The following code sample tests that multiple user variables (firstName, lastName, age, address, city, country) have the correct values.



import static org.junit.Assert.*;

import org.junit.Test;

public class AssertionTests {

private String generateFirstName()   
{ return "John";   }

private String generateLastName()    
{  return "Smith";  }

private int generateAge()            
{  return 32;       }

private String generateAddress()     
{  return "123 main street";  }

private String generateCity()        
{  return "Burnaby";   }

private String generateCountry()     
{  return "USA";   }

@Test
public void testUserDetailsWithMultipleAssertions()
{

String firstName = getFirstName();


String lastName = getLastName();

int age = getAge();

String address = getAddress();

String city = getCity();

String country = getCountry();

assertTrue("firstname is incorrect", 
firstName.equalsIgnoreCase("John"));

assertTrue("lastname is incorrect",  
lastName.equalsIgnoreCase("Smith"));

assertEquals("age is incorrect", age, 32);

assertTrue("address is incorrect", 
address.equalsIgnoreCase("123 main street"));

assertTrue("city is incorrect", 
city.equalsIgnoreCase("Burnaby"));

assertTrue("country is incorrect", 
country.equalsIgnoreCase("USA"));

}



The user variables' values are provided by utility methods (getFirstName(), getLastName(), getAge(), getAddress(), getCity()).

The test script works but it is too long and complicated for the simple thing that it implements.



Let's assume that it is possible to get a User object instead of getting firstName, lastName, age, address and city separately.

The user object is provided by a utility method called getUser().

It returns an object of  a User class:



public class User {

private String firstName;

private String lastName;

private int age;

private String address;

private String city;

private String country;

public User(String firstName, String lastName, int age, 
String address, String city, String country)
{

this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.address = address;
this.city = city;
this.country = country;

}

public String firstName() {
return this.firstName;
}

public String lastName() {
return this.lastName;
}

public int age() {
return this.age;
}

public String address() {
return this.address;
}

public String city() {
return this.city;
}

public String country() {
return this.country;
}

}


The User class is very simple:
  • The constructor has parameters for firstName, lastName, age, address and city and saves them in the class's fields
  • The class has get methods for its fields

Having the User class, we can use an object in the test script instead of separate variables:


import static org.junit.Assert.*;

import org.junit.Test;

public class AssertionTests {

private User getUser()
{

return new User("John", "Smith", 32, "123 main street", "Burnaby", "USA"

}

@Test
public void testUserDetailsWithMultipleAssertions()
{

User user = getUser();

assertTrue("firstname is incorrect",
user.firstName().equalsIgnoreCase("John"));

assertTrue("lastname is incorrect",
user.lastName().equalsIgnoreCase("Smith"));

assertEquals("age is incorrect",
user.age(), 32);

assertTrue("address is incorrect",
user.address().equalsIgnoreCase("123 main street"));

assertTrue("city is incorrect",
user.city().equalsIgnoreCase("Burnaby"));

assertTrue("country is incorrect",
user.country().equalsIgnoreCase("USA"));

} }




The test script looks a bit better when using the user object but it is still too long.



Create a custom assertion



The first way of simplifying the test script is with a custom assertion method.

Since we are using the user object, we can pass it to the custom assertion who will just include the individual assertions:




import static org.junit.Assert.*;

import org.junit.Test;

public class AssertionTests {

private void assertCorrectUserDetails(User user)
{

assertTrue("firstname is incorrect",
user.firstName().equalsIgnoreCase("John"));

assertTrue("lastname is incorrect",
user.lastName().equalsIgnoreCase("Smith"));

assertEquals("age is incorrect", user.age(), 32);

assertTrue("address is incorrect",
user.address().equalsIgnoreCase("123 main street"));

assertTrue("city is incorrect",
user.city().equalsIgnoreCase("Burnaby"));

assertTrue("country is incorrect",
user.country().equalsIgnoreCase("USA"));

}

private User getUser()
{

return new User("John", "Smith", 32, "123 main street", "Burnaby", "USA");

}

@Test
public void testUserDetailsWithCustomAssertion()
{

User user = getUser();

assertCorrectUserDetails(user);

}

}



The test script is very short when using the user object and a custom assertion.

But it lacks clarity since there is data hard-coded in the assertions that make the custom assertion.

We can remove the hard-coded data by creating a test object that uses the User class as well.

Then, we compare the user object with the test object in an assertion.




Compare the user object with a test user object


First, we will add a new method to the User class that compares 2 user objects:


public Boolean equalsTo(User anotherUser)
{

Boolean result = false;

if (
this.firstName().equalsIgnoreCase(anotherUser.firstName()) &&
this.lastName().equalsIgnoreCase(anotherUser.lastName()) &&
this.age() == anotherUser.age() &&
this.address().equalsIgnoreCase(anotherUser.address()) &&
this.city().equalsIgnoreCase(anotherUser.city()) &&
this.country().equalsIgnoreCase(anotherUser.country())
)

result = true;


return result;

}



The method is quite simple.

It gets a User object as parameter and compares the parameter's fields with the currect object's fields.

If all fields are equal, it returns true, otherwise false.

The test script gets its final version now:


@Test
public void testCorrectUser()
{

User user = getUser();

User testUser = new User("John", "Smith", 32, "123 main street", "Burnaby", "USA");

assertTrue(user.equalsTo(testUser) == true);

}




It first gets the user object.

Then, it creates a testUser object.

Finally, it asserts that the 2 objects are equal using the equalsTo() method of the User class.

Share this

2 Responses to "Have too Many Assertions in Test Scripts? Use Custom Assertions Instead"

  1. Thanks for appreciating. Really means and inspires a lot to hear from you guys.I have bookmarked it and I am looking forward to reading new articles. Keep up the good work..Believe me, This is very helpful for me.

    Digital marketing company in Chennai

    ReplyDelete
  2. Great and useful article. Creating content regularly is very tough. Your points are motivated me to move on.


    SEO Company in Chennai

    ReplyDelete