Make your test AAA using proper structure

The advantages of arrange-act-assert

Recently in my project I've had a task where I had to fix some SonarQube findings, in particular ones complaining that there are no assertions in some automated tests.

Zrzut ekranu 2022-02-18 o 11.01.49.png

Those were functional tests verifying that our component that is responsible for connecting to DB2 SQL database is actually working, can open a jdbc Connection and run some queries. Here you can find one of the tests that Sonar was complaining about:

@Test
public void testDB2Connection() throws SQLException {
    try (Connection c = component.getConnection();
         Statement s = c.createStatement();
         ResultSet resultSet = s.executeQuery("SELECT COUNT(1) FROM dummytable");
    ) {
        resultSet.next();
        int count = resultSet.getInt(1);
        System.out.println("Number of rows: " + count);
    }
}

As you can see, the test logic is pretty straightforward, we acquire Connection object, run some queries and print the results. However in terms of clear and uniform test structure this is far from perfect. Can you answer easily just by looking at the code what is actually tested here?

My answer is no, so this code inspired me to write a post related to test structure, in particular arrange-act-assert.

What's going on here?!

The previous example of badly structured code might not be sufficient enough to understand the problem so let me provide some additional example. Let's assume that we have an object that requires a lot of input to do it's job, say insurance fee calculator:

@Test
public void shouldCalculateInsuranceFee() {
  FeeCalculator systemUnderTest = new FeeCalculator();
  InsuranceProvider insurer = new InsuranceProvider(
    //... lots of initialisation code
  );
  InsuranceHolder theInsured = new InsuranceHolder(
    //... once again a lot of initialisation code
  );
  InsuranceFee result = systemUnderTest.feeFrom(insurer, theInsured);
  assertThat(result).isNotNull();
  //... some more assertions
}

I guess you can imagine a test scenario that is even more complex (especially when we're talking at a higher level test, not unit test). Often in complex test it's hard to tell what's going on. You could of course take your time, refactor the test code, extract some methods etc. But what if you don't have too much time at hand?

arrange-act-assert to the rescue

In the next snipped the only thing I changed is that I've added 4 line breaks. Is that better?

@Test
public void shouldCalculateInsuranceFee() {
  FeeCalculator systemUnderTest = new FeeCalculator();
  InsuranceProvider insurer = new InsuranceProvider(
    //... lots of initialisation code
  );
  InsuranceHolder theInsured = new InsuranceHolder(
    //... once again a lot of initialisation code
  );


  InsuranceFee result = systemUnderTest.feeFrom(insurer, theInsured);


  assertThat(result).isNotNull();
  //... some more assertions
}

That's the whole point! With this simple trick we already increased the readability of our test, taking almost no effort at all. Using arrange-act-assert means only that your test should be divided in three sections:

  • Arrange section, where you set up your test:
    • Introduce Collaborators (mocks etc.)
    • Create System Under Test (tested object/component/service/etc.)
    • Configure Collaborators behaviors'
    • Do anything that's required before the actual execution of test
  • Act section, where you perform the operation that you want to test. Usually this means invoking a method on some object
  • Assert section where you verify the correctness of the operation performed in previous step such as:
    • Checking if the returned result meets specified criteria
    • Checking side effects - verifying if the Collaborators where invoked as specified.

So now, here's a challenge for you - next time you see a test that makes you think This is messed up, try to rearrange the lines so that it's divided into those 3 sections and tell me how it went. And may all of your test refactorings be nice and smooth!

tried-to-refactor.jpeg