Idea why

Why would you want to do automated unit tests? The main reason is laziness

First explain the background, for the convenience of subsequent expansion:

  • In October 2020, the platform side began to migrate back-end interfaces to the BFF layer (hereinafter referred to as the “Project”).

  • Project code includes multiple directions: creation, BP, data reports & tools, promotion management, material library

  • The project code is written using TS, and the perfection of TS definition varies in different directions (global TS definition specification is not restricted)

  • Project interaction with the back-end interface: 95%+ in RPC mode, very few in HTTP mode

  • The project uses GULU framework (company internal framework, similar to Egg), file structure includes: Router, Controller, Service, etc

    • The request invocation sequence is: front-end request -> Router -> Controller -> Service

    • There is no reverse call

    • The division of labor between controller layer and service layer is preliminarily clarified:

      • The Controller layer is responsible for: permission verification, request input processing, result formatting return
      • The Service layer is responsible for back-end RPC interaction, data clipping, and stitching
  • The project does not use redis/DB storage

  • The project does not contain unit tests

Handwritten phase

There is no unit test to do the quality assurance of the code, not at ease. Write unit tests specifically for the Service layer in the handwriting landing phase of unit tests.

After some hand-written attempts, some old problems with unit tests on the ground were exposed:

Not familiar with: not familiar with jEST usage, not familiar with GULU test writing, not familiar with how to write tests

Performance measurement: Single test is not part of the business and is not motivated enough. There is also a lack of indicators for measuring single test effect

Human effort: For beginners, writing a single test with 80% coverage is about a third of the development time

Based on these problems, the enthusiasm to write a single test is not too high. You have to push down from the top. Start thinking: Can unit tests be automated?

bud

If you look at the entire project (hereinafter referred to as the “Service layer project”) from the service layer as an entry point, you can get a parse diagram of internal and external dependencies:

  1. Function code has TS type definitions (with varying degrees of refinement). Mock data can be derived by using the TS type definition, that is, mock out the corresponding function into the parameter data
  2. User information can be written to death, and usually does not need to be changed
  3. RPC information is available through the official GULU plug-in@byted-service/rpc-mockformock

Thinking about it this way, you can conclude that all external dependencies (inputs) of a Service layer project are mock able.

Think of a Service layer project as a black box where the output should not change while the input does!

The specific plan

After full discussion of the above budding ideas, an overall logical diagram can be obtained:

Scheme Details

Gets the function input parameter type and Mock data

The function input and output parameter formats can be obtained with the TS compiler capability.

Mock data is available with the tools typescript-json-schema and jSON-schema-faker. Typescript-json-schema supports the latest TS format, which meets the daily TS format requirements.

Environment to build

Prerequisite: The test framework of the project under test (based on Gulu) has been built.

Build the environment with the help of the configured test framework, without directly invading the existing code logic:

import { getFrameworkPath, restore } from './lib/util'; import { HttpApplication, HttpApplicationOptions } from '@gulu/application-http'; const frameworkName = '@gulu/application-http'; const initCwd = process.cwd(); Export const app = function (options: HttpApplicationOptions = {}): HttpApplication { const root = options.root || initCwd; Const {HttpApplication: Application} = require(getFrameworkPath(frameworkName, root)); const application = new Application({ ... GuluEnv: {name:'test',value:['test']}, root,}); application.load(root); application.listen(0); return application; }; // call app const mocker = app(); await mocker.ready(); const ctx = mocker.createMockContext();Copy the code

Insert pile setting

The most common position placement tool for the front-end is Istanbul/NYC. It provides two ways of piling:

  • Compile-time staking: that is, coverage acquisition code is inserted during code translation, and the output code itself has acquisition capability.
  • Run time staking: that is, output code itself does not have acquisition capability, usedhookRequireMethod (this method uses Nodejs Module loading mechanism, hook the file introduced by require, return the file after plugging). Therefore, it needs to be introduced before the business code requires.

This scheme is carried out by running pile insertion:

import NYC from 'nyc'; const n = new NYC(config); n.reset(); n.wrap(); If (coverageImproved) {n.ritecoveragefile (); if (coverageImproved) {n.ritecoveragefile (); }Copy the code

Test case generation

Assertion results

Test cases are divided into three stages: Arrange resources, Act, and Assert. In automated unit tests, the assertion results are taken by toMatchObject() to determine whether the output actually executed has changed data (subset relationship) from the output of a single test.

Use case generation

Template replacement. Make a variable substitution by way of a marker substitution:

Describe ('$__className ', () => {it('$__functionName ', () => {let CTX = app.mockContext(); ctx.request = Object.assign(ctx.request, loginInfo); let resp = ctx.service.$__functionName$.apply(null,$__arguments$); expect(resp).toMatchObject($__respData$); }); });Copy the code

Landing & Challenge

The final output

  1. Develop and publish kobS (plug-in name) library to company NPM.
  2. Output an automated single test kobS usage guide.

The ground is

  1. Establish a new flow of NODE_core single test access CI
  2. It has been connected in two directions: data report and tools: codeLine coveragefor0% 88%

  1. Intercepted 2 cases of incorrectly modified codes due to single test

The Demo presentation

challenge

  1. How do you automate the testing process to ensure that normal business processes are tested?

During the automated testing phase, there is no guarantee that normal business processes will be safeguarded. Two points:

  • The responsibility for normal business processes is largely delegated to QA/ integration testing.
  • Automated testing can test some boundary cases that are not easily detected: increasing code robustness and ensuring the quality of code refactoring.
  1. How do you combine this tool with the steps of a manual handwriting test?

There have been attempts, but the final combination is not quite as expected. At this stage, a vscode plug-in tool is provided for developers to help them generate automated single test templates (including mock input parameters and RPC initial data).

Review of thinking

  1. Experience summary of project construction: the normative requirements of code writing and the importance of clear division of module structure
  2. Can we really stop writing unit tests? ❌
  3. Final solution positioning: assist in generating test tools.