Software development is still a young industry and it’s important that we (as software developers) should carefully consider ‘how’ we carry out the work we do.
We should work smarter not harder.
We all know testing is critical for the production of valuable software. But often our approach to testing is overly simplistic driven by mental models that lack nuance.
The testing triangle is a mental model to describe how much testing we need in an app. It helps us decide specifically how much of each ‘type’ of test we need.
At the bottom we have unit tests (lots of them).
At the top we have end-to-end (E2E) tests (just a few of them).
Typically, as a codebase grows it will become more bloated and less flexible.
We can often end up with no option but to use E2E tests to test certain features in our app confidently when the other types of tests don’t cover ‘enough ground’. This means over time the ratio of dependence on E2E testing goes up in comparison to the lower part of the triangle. In our book (Think! Like a UI Architect!) we call this the E2E testing triangle inversion paradox.
Consider the following:
var customerPageHeading = element.find('section').find('li').where(css['color=black']); expect(customerPageHeading).toBe('Dave Smith');
In this E2E test we want to check that our view says ‘Dave Smith’ somewhere (see the last line).
However, we ended up having to trudge through complex HTML just to find what we are looking for (see the selector on the first line).
So, our E2E test is gaining deep coupling to the structure of our HTML. On the face of it this ‘seems’ fine. After all, it gives us ‘confidence’ that our view works.
But over time tests like this become painful to read and to maintain because they test too many things (HTML and data) and they break the fundamental principle of Separation of Concerns (SOC). In SOC we want parts of our system to address separate concerns only.
The reason we normally test HTML and data like this is to validate our complex rendering logic (React, Angular or Vue component markup).
We do this because we want certainty that the procedural logic buried in the markup/component (conditional logic, forEach loops, iterators, Booleans etc) actually works.
But here’s the secret about E2E testing that nobody told you.
Our markup should not be so complicated that it NEEDS testing.
Instead, we should have markup that is very, very simple.
In our UI Architecture Academy we teach engineers how to make simple markup by using the concept of ‘flat presentation’. Flat presentation lets us avoid testing html and data by pulling that data out of the html and putting it into an abstraction.
When we do this, we can test the abstraction instead of the data and the HTML. Thus, we end up with heavily simplified testing which avoids all of the HTML noise…
var customerPageHeading = customerPage.mainTitle; expect(customerPageHeading).toBe('Dave Smith')
In order to create ‘simple’ markup with flat presentation over the long term in our components we must observe rules as we build the code. The rules that we build this code with is called the architecture. When we optimise around creating an architecture in a UI app that is easy to test through mechanisms such as we saw above, we call it a ‘testable UI architecture’.
This testable UI Architecture will give us numerous benefits such as, more stable interfaces in our internal design, more modular features, and it will allow us to cover much of the same ground we would with E2E testing but without needing to couple to the HTML. For example it may let us test many specifications of our app just using a simple unit tests and a simple unit testing framework.
No, this article isn’t saying E2E testing isn’t important or valid.
Instead, it is designed to get you to think about creating an architecture which will allow you to reduce your dependence on E2E tests by having better architecture.
Because remember; if all you use is a hammer, then everything looks like a nail.
E2E tests are cumbersome, but they are also valuable.
This article is trying to make you think about focussing on architecture which reduces your dependence on them so that when you do really need to use them, they MATTER!
In this article we have seen how we can be forced to test data and markup all together when we think E2E testing is our only option.
With this limited and one-size-fits-all approach we can end up breaking the very fundamental software design principal SOC which means we end up with a bad UI architecture.
Instead we should focus on a testable UI architecture that lets us create better and simpler tests at the code level, meaning that we reduce our dependence on E2E tests. Although seeming counter intuitive this will actually drive up the quality of the E2E tests we do end up using because they will have a higher reward to effort ratio.
Download our 28-page book exploring big picture ideas to help you create better architecture with any UI framework (inc. React, Angular or Vue).