Friday, November 17, 2017

[Learning Notes]: TDD for React/Redux in an Isomorphic Application by Hany Elemary

I felt lucky I managed to watch this video before our Safari Books Online subscriptions were discontinued.

History

Hany Elemary begins with a little history of how isomorphic applications came to be. They evolve roughly as follows:
  1. Full-page refresh
    • Thin client (DOM manipulation, form validation, animations)
    • Rich server (routing, views, application logics, storage)
  2. Rich Internet application with AJAX request
    • Rich client (DOM manipulation, form validation, animations + views, application/display logic)
    • Thin server (initial bootstrap, API, storage)
  3. Single Page Application Isomorphic
    • Client (DOM manipulation, form validation, animations)
    • Shared code (routing, views, application/display logic) => runs client-side or server-side based on the request's context
    • Server (API, storage)
To make it easier for beginners to understand the differences between these developments, Hany mentions an analogy of a server in a restaurant and what they do when a customer orders a dish:
  1. Full-page refresh
    • First order: go buy pots pans, groceries, come back, cook & serve
    • Subsequent orders: go buy pots pans, groceries, come back, cook & serve
  2. Rich internet application with AJAX request
    • First order: go buy pots pans, come back, go get groceries, come back, cook & serve
    • Subsequent orders: go get groceries, come back, cook & serve
  3. Single Page Application Isomorphic
    • First order: go buy pots pans, get groceries, come back, cook & serve
    • Subsequent orders: go get groceries, come back, cook & serve

Sequence diagram of isomorphic application

Initial request
  1. User enters URL in browser
  2. Browser requests data from server
  3. Server fetches data from API/Database
  4. API/Database returns data to server eg. JSON
  5. Server generates & serves markup back to browser
  6. Browser render page
Subsequent requests
  1. User navigates in browser
  2. Browser fetches data straight from API/Database
  3. API/Database returns data to browser
  4. Browser renders page

React/Redux Overview

Paradigm shift in front-end development

  • React is library NOT framework
  • React only handles view layer eg. no AJAX, no Promises, no event system, etc. So why do we need it? Well sometimes we only need small composable library rather than a big ecosystem where you only use a tiny fraction of it. Get rid of libraries you don't need and get new libraries when you need it
  • React is component-based architecture: reusable components
  • Web developers often told not to mix HTML with JavaScript because HTML cannot be minified and it also cluttered your components. React uses HTML within JavaScript (JSX). JSX is pre-processor step that compiles your HTML into JavaScript so it could be minified.
  • Virtual DOM and diffing algorithm. Virtual DOM is in-memory representation of the real DOM, it has no knowlege to browser or environment it's run in, it's essentially a data structure. Diffing algorithm makes sure only the difference between virtual and real DOM is committed to the real DOM.

Deterministic view rendering

We know exactly which view is rendered first. In asynchronous function we won't know, whichever request got a response first, it'll be rendered first (race condition) ie. it's non-deterministic.

Event Sourcing

Event sourcing is technique that cares about sequences of events that lead to a specific outcome rather than the outcome itself. We want to store all events that occurred through our system as opposed to the outcomes that have happened. Characteristic: no update, no delete, it's an append-only log of events. In accounting terms: general ledger. Your book of records becomes stream of events, your source of truth. Advantages:
  • We have audit trail of all events that have happened in our system
  • You can replay a sequence of events to bring the application to a very specific state

Redux was inspired by Event Sourcing

  • Single source of truth: Redux Store
  • State is read-only/immutable. We only appending things, we're not updating or deleting things
  • Changes (ie. actions not events!) are made with pure functions (reducers). Reducers: given the same parameters will always output the same result, no side effect, no network operation, etc

Developer's workflow

  1. Write action / action creator
  2. Write component reducer to return the new state
  3. Determine the shape of your state / data - root reducer
  4. Write component that dispatches your action
  5. Connect component to store to subscribe to changes

TDD with React/Redux - Simple Components

Test pyramid

  1. UI
  2. Driven through the browser. Ensure the app is healthy in very high level, only test business critical features that represent overall workflows. Tend to be very slow and very expensive to maintain.
  3. Service (service level test, contract test)
  4. Expecting specific response for API given specific request. If API changes, test should fail. Protecting your application from changes in downstream dependencies or if your application is dependent on an API that you don't own.
  5. Unit
  6. Responsible for example display logic, behaviour, integration between components. Should be very fast and inexpensive to maintain.

Testing framework

  • Mocha - Test framework: assertions, expectations
  • Sinonjs - Stubbing framework: fake and spy on function calls
  • Mochawesome - Reporting: HTML reports that generate with each run, important to integrate with CI/CD pipeline
  • Enzyme - React test utility: rendering mechanism, access to components instances and children
  • Istanbul - Reporting: code coverage

TDD with React/Redux - Async Operation

Redux Saga

  • A library that aims to make side-effects easier and better.
  • What is side effects? network / asynchronous operations like fetching data. We can't guarantee that the same request will always be successful.
  • Reads like synchronous code. Think of Redux Saga as separate thread in your application.
  • ES6 generators makes an asynchronous code appears synchronous.
  • Redux Saga is a middleware. (PS: middleware is some code you can put between the framework receiving a request and the framework generating a response eg. logging)

Testing frameworks

  • Nock - HTTP mocking: useful for testing components integration with APIs, E2E tests
  • Mountebank - HTTP stubbing over the wire: useful for testing components integration with APIs, E2E tests, simulating failure modes.
  • Mountebank allows you to create an actual server and then mock the HTTP request and response. You can do pattern matching ie. if I see this pattern in the request then return this particular response. Another feature is latency ie. this request will return this response after n seconds. This can be used to simulate loading in your application.

No comments:

Post a Comment