introduce

Unit testing is the process of breaking an application down into the smallest possible functions and creating repeatable, automated test cases. Without unit testing, infrequently used functions can be as long as a tree without finding bugs, especially when a common method is called in multiple places in the business code. When developers modify this common method, there is no way to ensure that the changes are synchronized in each place. There may also be bugs after the project is launched due to incomplete testing, which is very dangerous and troublesome. Instead, by using unit tests, we can verify the functionality of each system function before any code is merged into the main branch, and problems can be found automatically and in a timely manner, rather than waiting until the code is actually in production.

  • Why unit tests?

GitHub frameworks and plug-ins will not be trusted by developers without test cases, and developers will be very careful when selecting or even adopting them, which shows how important testing is. The following documents the thoughts and thoughts on how projects generated by @angular/ CLI scaffolding are tested and the testing process.

  • How much test coverage is enough?

Most of the time you may not actually or budget to write 100 percent coverage test sets for existing functionality, but even a single test can contribute value to system building. Therefore, when deciding where to start writing unit tests, start where you can get the most out of them. Once you have a test set that provides basic coverage, you can start looking for the most critical parts of your system, or the parts that have broken down frequently in the past, create requirements for each of them in the requirements list, and make sure you push those requirements as quickly as possible. For example, I would add tests to the public method of the project THAT I am currently developing, and I would add tests to the business that involves money manipulation.

  • “Do one thing at a time, and do it well” is the rule for building unit test-based applications.

1. Advantages of testing

  • To ensure the stability and feasibility of the code,
  • A test is a demo, such as testing a method, that shows how the method is used.
  • Even if you spend more time writing test code, you’ll actually save yourself a lot of time chasing down bugs later.

2. Test rules

  • It is the generic, public Utils function that must be tested.
  • Complex interactions require some testing.
  • Network requests can be submitted to contract tests or not tested at all.

3. Test the configuration

Projects created with @angular/ CLI generate Jasmine and Karma configuration files for you to test immediately. Type the following code:

ng test
Copy the code

Or configure the following code in package.json by typing NPM run test-coverage, note –watch is set to true to monitor the test code in real time.

 "scripts": {
		"test-coverage": "ng test --code-coverage --watch=true"
 }
Copy the code

The karma. Conf.js file is part of the karma configuration file. @angular/cli builds the complete runtime configuration in memory, based on the project structure specified in the angular.json file and the karmap.conf.js file.

  • karma.conf.jsThe configuration file
// Karma configuration file, see link for more information
/ / https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: ' '.frameworks: ['jasmine'.'@angular-devkit/build-angular'].plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular-devkit/build-angular/plugins/karma')].client: {
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    coverageIstanbulReporter: {
      dir: require('path').join(__dirname, './coverage/fc-angular'),
      reports: ['html'.'lcovonly'.'text-summary'].fixWebpackSourcePaths: true
    },
    reporters: ['progress'.'kjhtml'].port: 9876.colors: true.logLevel: config.LOG_INFO,
    autoWatch: true.browsers: ['Chrome'].singleRun: false.restartOnFileChange: true
  });
};

Copy the code
  • test.tsThe configuration file
getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('/'.true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

Copy the code
  • What testing frameworks are used?

4. Introduction to Jasmine Testing framework

Creating a Test file

For example, if I want to test common.service.ts, create a new common.service.spec.ts file in the current folder of the file. After you create a new file, start testing!

The extension of the test file must be.spec.ts so that the tool can recognize it as a test file, also known as a spec file.

Jasmine is a behavior driven development testing framework for JavaScript. It doesn’t rely on a browser, the DOM, or any JavaScript framework. Therefore, it is suitable for websites, Node.js projects, or anywhere JavaScript can be run.

How to configure this in node.js

Add Jasmine to package.json

npm install --save-dev jasmine
Copy the code

To initialize Jasmine in your project, type the following command:

node node_modules/jasmine/bin/jasmine init
Copy the code

Set jasmine as your test script in package.json

"scripts": { "test": "jasmine" }
Copy the code

Type the following command to run your test code

npm test
Copy the code

Jasmine common API

Jamine official documentation API

4.3.1 afterAll (function, the timeout)

Ensure that it is invoked after all IT execution in Describe is complete.

4.3.2 afterEach (function, the timeout)

Injection after test.

4.3.3 beforeAll (function, the timeout)

Ensure that all IT is invoked before it is executed in Describe

4.3.4 beforeEach (function, the timeout)

Injection before test.

4.3.5 the describe (description, specDefinitions)

Represents a set of similar test cases, and then the specific test case starts with IT

  • Parameters: | Name | Type | Description | | :— | :— | :— | | description| String | group text | |specDefinitionsThe function called | function | Jasmine will define internal | suites and test cases

4.3.6 nested the describe

BeforeEach is executed from the outside in for different levels of IT execution, and afterEach is executed from the inside out at the end of each IT execution.

4.3.7 expect (actual) – > {matchers}

Create an expected value for the test case and verify the actual calculated value of the expected value.

  • The Parameters: | Name | Type | Description | | : – | : – | : – | | actual | Object | test expectations actual calculated value. |

  • Returns:

Type matchers

  • The sample
describe('commonService'.(a)= > {
  / / test getGuid ()
  describe('getGuid() function'.(a)= > {
    let getGuidTest1 = CommonService.getGuid();
    let getGuidTest2 = CommonService.getGuid();
    let getGuidTest3 = CommonService.getGuid();
    it('should get a guid'.(a)= > {
      expect(getGuidTest1.length).toBe(36);
      expect(getGuidTest2.length).toBe(36);
      expect(getGuidTest3.length).toBe(36);
    });
  });
});
Copy the code

4.3.8 expectAsync (actual) – > {async – matchers}

Create an asynchronous expectation, noting that the matches provided by the asynchronous expectation will all return promises that must either be returned from the specification or await with await so that Jasmine associates them with the correct specification.

  • The Parameters: | Name | Type | Description | | : – | : – | : – | | actual | Object | test expectations actual calculated value. |

  • The sample

await expectAsync(somePromise).toBeResolved();
return expectAsync(somePromise).toBeResolved();
Copy the code

V4.3.9 fail (error)

Explicitly marking test cases as failures.

  • Parameters | Name | Type | Attributes | Description | | :— | :— | :— | :— | | error| String | Error | | | failure reasons

4.3.10 fdescribe (description, specDefinitions)

If there are suites or test cases, only those suites or test cases are executed.

  • Parameters:
Name Type Description
description String A written description of the group
specDefinitions function The functions called by Jasmine define the internal suite and metrics

4.3.11 fit (description, testFunction, timeout)

An important point is that if the suites or specifications are centralized, only those suites or specifications are executed.

  • Parameters: | Name | Type | Attributes | Default | Description | | :— | :— | :— | :— | :— | | description| String | | | text description of the specification is checking. | |testFunction | implementationCallback| | | containing test code function. | |timeout | Int | | jasmine.DEFAULT_TIMEOUT_INTERVAL| asynchronous specification custom timeout. |

4.3.12 it (description, testFunction, timeout)

Define a test case. Use cases should contain one or more expectations for testing code state. Specifications that expect all to succeed will pass, and tests that fail will fail. Its name is a synonym for the object of the test, not an acronym for anything. You can make the specification more readable by concatenating the function name and parameter description into a complete sentence.

4.3.13 pending (the message)

Mark the specification as pending and the expected results will be ignored.

  • Parameters: | Name | Type | Attributes | Description | | :— | :— | :— | :— | | message| String | | test to be determined. |

4.3.14 spyOn (obj, methodName)

Installs indirectly on existing objects.

  • The Parameters: | Name | Type | Description | | : – | : – | : – | | obj | Object | want to install on the indirect Object. | | methodName | String | use indirect instead of the name of the method. |

  • Returns

Type    Spy

4.3.15 spyOnAllFunctions (obj) – > {Object}

Installs indirect programs on all writable and configurable properties of the object.

  • The Parameters: | Name | Type | Description | | : – | : – | : – | | obj | Object | install | indirect Object

  • Returns

The Type of Object

4.3.16 spyOnProperty (obj, propertyName, accessType) – > {Spy}

Properties installed with Object.defineProperty are indirectly installed on existing objects.

  • Parameters: | Name | Type | Attributes | Default | Description | | : – | : – | : – | : – | : – | | obj | Object | | | installed in the indirect Object | | propertyName | String | | | with indirect replace the name of the attribute. | | accessType | String | | get | this property to Spy the type of access (get) | set |

  • Returns:

Type Spy

4.3.17 xdescribe (description, specDefinitions)

With descriptions temporarily disabled, specifications in Xdescribe are marked as pending and will not be executed.

  • Parameters: | Name | Type | Description | | :— | :— | :— | | description| String | group text | |specDefinitionsThe function called | function | Jasmine will define internal | suites and test cases

4.3.18 xit

Temporarily disable it and it will not be executed.

4.3.19 toBe(Similar to= = =)

expect(true).toBe(true);
Copy the code

4.3.20 toEqual(Comparing the value of a variable literal)

expect({ foo: 'foo'}).toEqual( {foo: 'foo'});Copy the code

4.3.21 toMatch(Matching Values and regular expressions)

expect('foo').toMatch(/foo/);
Copy the code

4.3.22 toBeDefined(Check whether variables are defined)

var foo = {
    bar: 'foo'
};
expect(foo.bar).toBeDefined();
Copy the code

4.3.23 toBeNull(Check whether the variable isnull)

var foo = null;
expect(foo).toBeNull();
Copy the code

4.3.24 toBeTruthy(Check whether the variable value can be converted to a BooleantrueValue)

expect({}).toBeTruthy();
Copy the code

4.3.25 toBeFalsy(Check whether the variable value can be converted into a Boolean typefalseValue)

expect(' ').toBeFalsy();
Copy the code

4.3.26 toContain(Check whether an element is contained in an array)

expect([1.2.4]).toContain(1);
Copy the code

4.3.27 toBeLessThan(Check whether the variable is less than a certain number)

expect(2).toBeLessThan(10);
Copy the code

4.3.28 toBeGreaterThan(Check whether the variable is greater than a certain number or variable)

expect(2).toBeGreaterThan(1);
Copy the code

4.3.29 toBeCloseTo(Compare whether two numbers are equal after reservating several decimal places, for accurate comparison of numbers)

expect(3.1).toBeCloseTo(3.0);
Copy the code

4.3.30 toThrow(Check whether a function throws an exception)

expect(function(){ return a + 1; }).toThrow();// true
expect(function(){ return a + 1; }).not.toThrow();// false
Copy the code

4.3.31 toHaveBeenCalled(Check whether a listening function is called)

4.3.32 toHaveBeenCalledWith(Check the matching information of parameters when listening to function calls)

5. Test code

5.1 Method Test

For example, I tested getGuid() three times. Although these three times are not 100% accurate, they are much more reliable than not testing. Now I’m thinking, at least how many times does the bottom level of a project have to be tested?

describe('commonService', () = > {/ / test getGuid ()
  describe('getGuid() function', () = > {let getGuidTest1 = CommonService.getGuid();
    let getGuidTest2 = CommonService.getGuid();
    let getGuidTest3 = CommonService.getGuid();
    it('should get a guid', () => {
      expect(getGuidTest1.length).toBe(36);
      expect(getGuidTest2.length).toBe(36);
      expect(getGuidTest3.length).toBe(36);
    });
  });
});
Copy the code

Executing the ng Test console produces the following code:

Chrome 80.0.3987 (Mac OS X 10.14.6): Executed 1 of 1 SUCCESS (0.014secs / 0.001secs) TOTAL: 1 SUCCESS TOTAL: 1 SUCCESSCopy the code

See the TOTAL: 1 SUCCESS lines? Indicates that a total of 1 methods were tested and 1 passed. If this method fails the test, code like the following appears, writing a description during the test and quickly locating the problem if the test fails.

Chrome 80.0.3987 (Mac OS X 10.14.6) commonService getGuid() Function should get a GUID FAILED Error: Expected 36 to be 35. at <Jasmine> at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/webpack:/src/fccore/service/common.service.spec.ts:21:35) at ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-evergreen.js:359:1) at ProxyZoneSpec.push.. /node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-testing.js:308:1) Chrome 80.0.3987 (Mac OS X 10.14.6) : Executed 1 of 1 (1 FAILED) (0 secs / 0.1 secs) Chrome 80.0.3987 (Mac OS X 10.14.6) commonService getGuid() Function should get a guid FAILED Error: Expected 36 to be 35. at <Jasmine> at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/webpack:/src/fccore/service/common.service.spec.ts:21:35) at ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-evergreen.js:359:1) at ProxyZoneSpec.push.. /node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/webpaChrome 80.0.3987 (Mac OS X 10.14.6): Executed 1 of 1 (1 FAILED) ERROR (0.115 secs / 0.1 secs)Copy the code

Interface/contract testing

Service test

Component test

Establish a continuous integration environment

The Continuous integration (CI) server lets you configure your project’s code repository so that your tests run every time you submit and receive a Pull Request. There are already paid CI servers like Circle CI and Travis CI, and you can use Jenkins or other software to build your own free CI server. Although Circle CI and Travis CI are paid services, they also provide free services for open source projects. You can create public projects on GitHub and enjoy these services for free. When you contribute code to the Angular repository, the entire test suite is automatically run with the Circle CI and Travis CI. If I had to choose, because the company’s code needs to be closed source and cost saving, I would choose Jenkins for free. While GitHub’s code is open source, I would choose Travis CI.

The performance test

Visual restoration test

Red Hat test methods

The resources

  • Angular Chinese – Testing knowledge introduction
  • Angular official website test demo code
  • Ng.ant. Design unit test
  • Wechat reading “Front-end Architecture: From Entry to Micro Front end” – by Fengda Huang
  • Jasmine Chinese Guide – Cloud Community
  • Design of Front-end Architecture. By Micah Godbolt

Don’t be afraid to ask for help, don’t be afraid to share your knowledge, and don’t be afraid to stand up and encourage others to take up the field. Finally, whatever you do, under no circumstances should you be afraid to write it all down.