Testing Frontend Apps — what, where, how?
By: Mladen Despotovic
Tags: Frontend Testing, Acceptance Testing, Unit Testing, Frontend Development, Software Test Automation

Divide & Conquer

The fundamental principle of solving any big problem is to split it into several smaller ones, iterating the process until the solution becomes at least easy if not trivial.

Sure. This sounds like a Unit Test at the end, at least when we’re talking about the unit size… Wrong. I’m not talking about unit tests but rather about the architectural layering of the problems. We shall focus on 3 basic layers of our apps and acknowledge separation of concerns between them.

I’m talking about a well-executed Frontend Test Strategy and Approach, which considers the following preconditions and assumptions:

You don’t test backend services. You assume they are being tested and delivering within their contract both, functional and non-functional features. You mock them when needed. All the business logic, that is not implemented in the Backend services, is implemented in BFF. Frontend contains Presentation Logic only, no orchestration, no integration

Oh, just as a reminder, we shall leave a Clean Architecture diagram here, so we can relate to some terms further below:

Clean Architecture

The diagram below shows the testing path of 6 different types of recommended tests on 3 different architecture layers.

Testing on the same layer only:

Unit Tests

Module Tests

Module Integration Tests

UI Integration Tests

Backend Service Integration Tests

Across 2 layers:

Integration Tests

Acceptance Tests

Across the Full Stack:

End-To-End (E2E) Tests

The Frontend

Here you could test the following:

UI Components

If you use any kind of proprietary and complex UI components and controls, that are used at many places, then you should test them in isolation.

GOAL: Testing universal functionality and behaviour of reusable components, which, if broken, can cause havoc across the app(s) if reused massively.

UI Integration

This is the most important part of the Frontend testing. You should test with UI Automation tools all the user’s scenarios and journeys. This is by no means Integration or E2E Test!

The code in the Presentation Layer should by no means contain any Application Logic, any orchestration, any case at all, where for fulfilling a simple scenario multiple calls to multiple APIs on multiple Architecture Layers would be needed. If you see this in your Presentation layer, then you should move it to BFF, where it belongs.

Properly written Epics and Stories with clear Acceptance Criteria will most probably in some cases reflect directly into a test scenario, but they will in any case make it very easy to create.

The most important to understand here is that our Frontend code should not target any live APIs! All the calls should be stubbed, thus testing the Frontend code in isolation, making it easy to locate the problem if appears, separating it in UI and Presentation layer, separating it from Domain and Application Logic. UI Integration tests target the code, which was created by Frontend developers.

GOAL: Making sure all scenarios are properly implemented with UI and Presentation logic, isolated from dependencies to localise the problem not only to a certain architecture layer but to a certain mission team and technology experts.

Acceptance

These are the tests that tell us if our Client works or not, therefore it is very useful and appropriate for Regression tests. It should follow the principles below:

Targets all the code excluding backend services

Each test should implement acceptance criteria of the test case that reflects one of at most very few stories. It’s a great way of proving how good the stories are

Should never target the live Backend services and APIs, but rather the stubbed Backend

GOAL: To test all the Application code, proving all user scenarios are functioning in the fastest and most automated way. Very appropriate for daily regression testing in different environments.

Units of Code

As you are already used to, you can test your code on the smallest unit level. The question is, of course, considering what we have been talking about, how far should you go? Let us talk about that later.

The BFF

Modules and their behaviour

Since the BFF contains Application Logic, it makes all the sense to test the modules of the code, but independently of potential backend services. Each module needs to be tested against expected behaviour in isolation, meaning, dependencies, like other modules, should be mocked, or in this case better implemented as stubs.

You can also test multiple modules against certain scenarios and in this case not using the stubs all the time. However…

I would always recommend Top-To-Bottom, Outside-In, London Style approach and replace the stubbed modules as I go along down to the bottom.

GOAL: Very similar to UI Integration at the Frontend with the difference, that the Application Logic is tested here.

Units of Code

As mentioned before, I will tackle the topic later.

Integration

That one is crucial for BFF! It tests the integration where it is most important to work properly: between Domain and Application!

Passing the test in this layer confirms, that your application works correctly and can work with multiple Clients and UIs.