How To Test 'State' In Your UI App (React, Angular or Vue)

As a UI framework grows in popularity; so does the demand for skills in it.

Books, conferences, merchandise, and big corporate sponsorship (Facebook, Google) all feed the commercial complex that promotes them.

I love the time saving benefits that UI frameworks give us, but I also see that too much focus on the framework makes UI engineers build weak software because their code becomes too locked and coupled to the intricacies of them.

So when it comes down to testing and state, a few simple architectural rules can help us immensely.

Tools

Typically, when we want to solve the state issue in a UI app then we end up with a few ‘limited’ options. At the forefront of those options is Redux. Given the overarching dominance of the UI framework React we end up with implementations such as React-Redux.

But, React-Redux has a big problem can you see what it is from the following example?

import React from 'react'
import ReactDOM from 'react-dom'
import TodoApp from './TodoApp'
import { Provider } from 'react-redux'
import store from './redux/store'

const rootElement = document.getElementById('root')
ReactDom.render(
  <Provider store={store}>
    <TodoApp />
  </Provider>,
  rootElement
)

The above code should send alarm bells ringing!

If It doesn’t; then I am going to assume you haven’t seen SQL buried in HTML, which I came across over a decade ago when I was a .net UI engineer.

'SQL in HTML' is when your views have SQL buried in them; inline. It was horrible because there is an impedance mis-match between the two. The type of thinking you do for SQL is different to what you do for HTML since you are looking for different things from them. In one you want data, in the other you want elements on a page.

The React-Redux code above has the same problem we see when we bury SQL in HTML. It puts information about state in the html markup.

Think about it.

We want to store state in our app, so why would it make sense to bury this information in the view?

It’s done because Redux is asynchronous and needs to tell the view to update, so they build React-Redux as a binding to allow this to happen. Sounds fair, but… doing it this way is like using a sledgehammer to crack a nut and;

1) the views become too complex (HTML + state bindings) to develop
2) the state ends up being tightly coupled to the view and markup…forever
3) it’s almost impossible to test your state independent of the views

Testing

The key to being able to test UI app state comes primarily down to one single principle.

The separation of concerns (SOC).

The SOC tells us to keep things separate because when we do this, we make it easier to maintain.

But, by mixing state and markup; we violate this principle.

Principles like the SOC are designed to give you a warning flag BEFORE the problem causes you ‘pain’. If we want good testability for our state then we need to avoid tools like React-Redux - which makes us mix it all together!

So, in our course UI Architecture Academy the first thing we teach students is about separating the framework from the UI code. But what does this mean?

Well, we discuss something called the ‘framework trap’.

When we are ‘trapped’ in the framework, we end up with an architecture that looks like this…

When really, we want an architecture that looks like this…

When you make your UI app look like the second slide your code becomes isolated and independent because;

On the left side you have what we call the ‘True UI’ which is where the framework code lives (React, Angular or Vue). This is where you process UI specific concerns.

On the right you have your ‘True Application’ (just pure JavaScript or TypeScript modules). This is where you process your presentation and business logic.

True UI vs True Application

When we follow SOC then we end up with two apps in one.

One that has the UI, the other that has our app.

This means we actually need 2 x state mechanisms; one that we can use in our True App section AND one we can use in our True UI section.

Having separate state in each one gives us the SOC!

When we do this, we can now independently test either our UI (and include its state) or our App (and include its state too)!

This not only makes our tests simpler and more likely to be accurate but...fundamentally less coupled – which is a critical key of creating a testable architecture!

Conclusion

Engineers often overlook critical architectural UI concerns meaning they unwittingly mix UI state with application state when building Single Page Applications.

This means their structural approach has no clear distinction between the two and they often over-use tools like React-Redux. These tools often do one well but rarely do both well!

The solution to this problem is to;

1) Build an architecture that keeps the True UI (framework) separate from the app (presentation and business logic).
2) Isolate and test the True UI (framework) part of your app independently
3) Isolate and test the True App (presentation and business logic) with its own state using ‘specification’ driven testing

Using this approach increases the cohesiveness of your architecture and make testing because easier because you have clean UI tests that simply test UI state behaviours and then more substantial specification testing for the main bulk of your app and app ‘state’!

NOTE : in our course UI Architecture Academy we show you how to use Redux without the markup lock-in problem we see in this article.


Author: Pete Heard

Pete is the founder of Logic Room. He designs the companies core material for JavaScript architecture, test automation and software team leadership.

LinkedIn

Want to learn 5 critical lessons for framework-agnostic JavaScript UI app design and structure?

Download our 28-page book exploring big picture ideas to help you create better architecture with any UI framework (inc. React, Angular or Vue).

Download
Close

50% Complete

Book Trial

Please fill out your details if you would like to trial our system.