logo logo

Writing Business-Oriented Tests for Living Documentation

Writing Business-Oriented Tests for Living Documentation

There is a recurrent topic in the automation testing and BDD (behavior driven development) literature as to why tests should be written from a business perspective instead of a technical one. This post tries to represent why it’s a key element if we want to use our tests as living documentation for our product. This is one of the reasons why almost any testing framework or library have the famous Given/When/Then syntax:

  • Given I read about the benefits of business-oriented tests
  • When I start using Gherkin syntax
  • Then I’m not necessarily writing business-oriented tests

How many times have you found an example like this in the real world? 🤔

Scenario: Display testers information
  Given I navigate to URL http://example.com
  And I create a tester with the name testerFirstname testerLastname
  And I create a book written by testerFirstname testerLastname
  When I click on the top navbar
  And I click on the Testers option
  Then the URL is http://example.com/testers
  And the list of testers is displayed
  And the list of testers contains 1 record
  And the first column contains the tester's name
  And the second column contains the tester's last name
  And the third column contains the book info

From this feature file, we can extract that it is an E2E test that is run over a browser and is clicking in several places of the UI. It validates explicitly the URL as well as how you navigate to a specific section of your application. It also asserts how the information related to testers is rendered in a very specific table format 📄

💡 Is this test business-oriented? To answer this question, let’s say that we decide to change the way that the tester information is represented in the UI, and provide it in cards with the information of each tester instead of in a table. 

💡 Would this test change in that context? Yes. The last 3 steps must be rewritten completely. 

💡 But, is that UI modification changing our business rules? No. We are just providing the same information as before. We just updated how we present that information to the user to make it more accessible.

Let’s have a look at this other implementation:

Scenario: Retrieve testers information
  Given the tester testerA who have written the book bookA
  When an application user request the testers’ information
  Then testerA data contains the information related to bookA

This feature is not tied to the UI implementation at all. It’s documenting that our application has a feature where we provide the books written by a tester together with their data. If we change the URL, the way the information is represented in the UI, the number of clicks a user needs to perform to get to the information, or any other implementation detail, the test won’t change.

This is just a level of abstraction. In the test implementation, we will have several layers of abstraction for sure. In a UI E2E test suite, we can organize our tests. For example, with a layer that represents the UX and a layer that represents the UI in addition to the business one. 

This will allow us to have our tests more organized and have a clear separation of responsibilities, so we know where to look depending on the type of change we are implementing 🔍

Code example of this representation

Business Rule ⇒ UX ⇒ UI

Business layer (in a feature file)

When an application user requests the testers’ information

UX Layer (in the implementation of the steps)

When(/^an application user request the testers information$/, function () {
  AplicationLandingPage
      .visit()
      .openNavigationMenu()
      .selectOptionFromNavigationMenu('testers')
      .clickOnSearchTestersInformation();
});

UI Layer

const MENU_ICON= '.menu-icon';

export default class ApplicationLandingPage{

  static visit() {
      cy.visit('/home/');
      return ApplicationLandingPage;
  }

static openNavigationMenu() {
      cy.get(MENU_ICON).click();
      return ApplicationLandingPage;
  }
//...
});

Let’s forgot about UI E2E

It’s easier to see a good implementation of business-oriented rules in E2E tests from the UI layer, as this layer is often seen as the less technical one. The lower you go in the testing pyramid layers, the harder it is to see a proper implementation, losing the documentation and readability of business orientation.

Have you ever heard someone saying that an API test can’t be written without mentioning the endpoint paths, the payloads, fields to be asserted, etc? To sum up, it’s not possible to write a business-oriented test because the layer is too technical.

Have you ever heard the same arguments about component integration tests? And what about unit testing? I’ve heard those arguments several times, but it’s strange to find a scenario where it’s hard to express it as oriented to business rules.

If it’s hard, the test is probably not needed or the application itself needs to be refactored. Writing business-oriented tests is more of a practice matter 💪

Scenario: Retrieve testers information
  Given the tester testerA who have written the book bookA
  When an application user request the testers’ information
  Then testerA data contains the information related to bookA

Going back to our original feature, can we infer which layer of the testing will be running? No, we can’t, as it is decoupled from the technical implementation. Reformulating the question, why don’t we validate that business rule in all the places where it makes sense to be validated? What if we can test it on every single layer of the testing pyramid?

We already saw an example of UI E2E, but what if we move to another layer? How would it look like in unit tests?

//Java Unit Test (of the service class where a business rule is implemented)
@Test
@DisplayName("Retrieve testers’ information includes books")
public void BookIncludedInTheTesterInfo() {

  givenATester(testerAEntity);
  givenTheBookWrittenByTheTester(testerABook, testerAEntity);

  List<Tester> testers = whenAllTestersInformationIsRequested();

  thenTesterDataIncludesTheBook(testers, testerA, testerABook);
 
}

With the previous implementation, we are introducing this layer of business logic in a unit test, by encapsulating the technical details of the test implementation to private methods. In the Givens, we will prepare the mocks, and in the Thens, we will run the assertions.

Example of this implementation:

private void givenATester(TesterEntity tester) {
   when(testersRepository.findAll()).thenReturn(ImmutableList.of(tester));
}

private void givenTheBookWrittenByTheTester(Book book, TesterEntity tester) {
  when(booksClient.getBookByAuthor(tester.getFirstName(),tester.getLastName()))
        .thenReturn(book);
}

private List<Tester> whenAllTestersInformationIsRequested() {
  return testersService.getAllTesters();
}

private void thenTesterDataIncludesTheBook(List<Tester> testers, Tester tester, Book book) {
  assertThat(testers).containsExactly(tester.withBooks(book));
}

So we have the same business rule, represented by the same words, tested in the two extremes of the testing pyramid- Unit tests and E2E. In the implementation of each of the steps, we get the technical context and we implement accordingly. 

This can be applied to any other layer, let’s see an example in API:

  • Given methods will create the tester and the book by calling the appropriated endpoints
  • When method will perform the actual call to the endpoint under tests
  • Then method will run the assertions

Conclusions

This post tries to represent what we mean by using our tests as living documentation when practicing BDD. If we follow this approach in any test written, for any piece of code responsible to implement business logic, we will be able to understand the behavior of that code by reading the tests 👩‍💻

If we only keep it with the technical implementation, we’ll be able to understand what the code does, but not what is the expected behavior of the application.

About the author

Roman Segador

I’m passionate about quality, where I’ve worked for the last 14 years in many different roles, currently as Engineering Manager and Quality Specialist at Packlink. I always try to promote Modern Testing Principles. One of the key things of my love for Quality is the possibility that it brings to us for work with many different profiles, from Software Developers, Product Managers, Engineering Managers to any other role in a company.

 

Leave a Reply

FacebookLinkedInTwitterEmail