Jest provides all the testing tools that developers need such as coverage reports. Jest is a testing framework with almost zero configuration. Angular JEST unit tests are written in three steps: introduce the test, run the test, and compare to see if it meets expectations. The assertions in Jest use Expect, which takes a parameter, the result of running the test content, and returns an object that calls the matcher (toBe/…). The parameter of the matcher is the expected result, so that the result can be compared with the expected result, and we can determine whether it is correct.
Two mandatory methods: test method: Jest encapsulation test method, generally fill in two parameters, description and test method, expect method: expect method, is what method you call, what parameters passed, what expected method.
1 Commonly used JEST assertions
- Jest assertions are commonly used
// Create the module to be tested in SRC /functions.js
export default {
sum(a, b) {
returna + b; }}// Create test cases in the test/functions.test.js file
import functions from '.. /src/functions';
test('sum(2 + 2) 等于 4'.() = > {
expect(functions.sum(2.2)).toBe(4);
})
Copy the code
- Not: Allows you to test if the result is not equal to a certain value
// Create the module to be tested in SRC /functions.js
export default {
sum(a, b) {
returna + b; }}// functions.test.js
import functions from '.. /src/functions'
test('Sum (2, 2) does not equal 5'.() = > {
expect(functions.sum(2.2)).not.toBe(5);
})
Copy the code
- ToEqual: Simple type absolute match; Matching of complex type content results
// functions.js
export default {
getAuthor() {
return {
name: 'LITANGHUI'.age: 24,}}}// functions.test.js
import functions from '.. /src/functions';
test('getAuthor() returns objects of equal depth '.() = > {
expect(functions.getAuthor()).toEqual(functions.getAuthor());
})
test('getAuthor() returns different object memory addresses'.() = > {
expect(functions.getAuthor()).not.toBe(functions.getAuthor());
})
Copy the code
- ToHaveLength: handy to test whether the length of string and array types meets expectations
// functions.js
export default {
getIntArray(num) {
if (!Number.isInteger(num)) {
throw Error('getIntArray' only accepts integer arguments');
}
let result = [];
for (let i = 0, len = num; i < len; i++) {
result.push(i);
}
returnresult; }}// functions.test.js
import functions from '.. /src/functions';
test('getIntArray(3) should return an array of 3'.() = > {
expect(functions.getIntArray(3)).toHaveLength(3);
})
Copy the code
- ToThorw: This allows us to test whether the method being tested throws as expected, but when using it, note that we must use a function to wrap the function being tested, as getIntArrayWrapFn did above, otherwise the assertion will fail because the function was thrown
// functions.test.js
import functions from '.. /src/functions';
test('getIntArray passed floating point number '.() = > {
function getIntArrayWrapFn() {
functions.getIntArray(3.1);
}
expect(getIntArrayWrapFn).toThrow('getIntArray only accepts integers');
});
Copy the code
- ToBeNull () : matches null
- ToBeUndefined () : matches undefined
- ToBeDefined () : matches non-undefined
- ToBeTruthy () : The match is converted to true
- ToBeFalsy () : False after matching
- ToBeGreaterThan () : equivalent to greater-than
- ToBeLessThan () : corresponds to the less than sign
- ToBeGreaterThanOrEqual () : equivalent to the greater-than or equal sign
- ToBeLessThanOrEqual () : corresponds to the less than or equal sign
- ToBeCloseTo () : Resolves JS floating point errors
- ToMatch (regExp/ String) : Matches string fragments with regular expressions or strings
- ToContain () : matches an item in an array or Set
2 a mock function
When you do unit tests, what you want to test depends on something else, such as an asynchronous request, which depends on the network, which is likely to make the test ineffective. Can you turn dependencies into manageable content? That’s where mack comes in. Mack isolates the content of the test from its dependencies by replacing the dependency with something we can control. So how do you mock? Use the Mack function. In Jest, when we talk about mack, we’re really talking about using mack functions instead of dependencies. The Mack function is a dummy or dummy function, so the most important thing for it is to implement all the functions of the dependency, so as to play the role of replacement. Typically, mock functions provide three functions to implement the substitution: call capture of a function, set the return value of the function, and change the implementation of the original function. The easiest way to create a mock function in Jest is to call the jest.fn() method.
2.1 Function call capture
Capturing calls refers to whether or not the function is called, what the arguments are, and what the return value is. It is often used to test callback functions to simulate real callback functions.
// functions.js
export default {
forEachFun: (array: any[], callback: Function) = > {
array.forEach((i) = >callback(i)); }}// functions.test.js
import functions from '.. /src/functions';
test('forEachFun calls every method '.() = > {
const mockFun = jest.fn();
const testArr = [1.2];
functions.forEachFun(testArr, mockFun);
console.log(mockFun.mock);
// expect(mockFun.mock.calls.length).toBe(2);
expect(mockFun).toHaveBeenCalled();
expect(mockFun).toBeCalledTimes(2);
expect(mockFun).toBeCalledWith(1);
expect(mockFun).toBeCalledWith(2);
});
// The mockFun property is an object that prints the result:{calls: [[1 ], [ 2]],instances: [ undefined.undefined].invocationCallOrder: [ 1.2].results: [{type: 'return'.value: undefined}, {type: 'return'.value: undefined}}]Copy the code
Calls holds the state of the call. Calls is an array, and each call makes up one element of the array, in this case two calls, two elements. Each element is an array, which represents the parameters of a function call. Since each call passes a parameter to the function, the array has only one item. If there are more than one argument, the array is multinomated, in order of the arguments listed in the function. At that time, it can be asserted, function calls a few times, just judgment calls. Length. Expect mockFun. Mock. Calls. (length), place (2) is the assertion that function is called twice. Expcet (mockfun.mock. Calls [0][0]).tobe (1); Jest encapsulates the mock argument of a function and provides a simple matcher.
// To determine whether the mock function has been used
toHaveBeenCalled()/toBeCalled()
// To determine how many times the mock function has been called
toHaveBeenCalledTimes(number)/toBeCalledTimes(number)
// To determine whether a mock function is being used with a specific argumenttoHaveBeenCalledWith(arg1,arg2,...) /toBeCalledWith(arg1,arg2,...)Copy the code
Sometimes, because the backend is not well developed or the network has problems, you don’t want to call the function, so you can just get the return value of the function. For example, an asynchronous function simply returns an Observable, and there is no need to request the server.
test('Set function return value'.() = > {
// Return normal
const mockFun = jest.fn();
mockFun.mockReturnValue({name: 'shao'});
const result: string = mockFun();
expect(mockFun).toBeCalledTimes(1);
expect(result).toEqual({name: 'shao'});
/ / return observables
const observableFun = jest.fn();
observableFun.mockReturnValue(of({name: 'shao'}));
const observableResult: Observable<any> = observableFun();
observableResult.subscribe((res) = > {
expect(res).toEqual({name: 'shao'});
});
});
Copy the code
2.2 Use spyOn spy to test the service
Services are often the easiest files to unit test. Here are some synchronous and asynchronous unit tests for ValueService that don’t even need the help of Angular testing tools.
// Straight Jasmine testing without Angular's testing support
describe('ValueService'.() = > {
let service: ValueService;
beforeEach(() = > { service = new ValueService(); });
it('#getValue should return real value'.() = > {
expect(service.getValue()).toBe('real value');
});
it('#getObservableValue should return value from observable'.() = > {
service.getObservableValue().subscribe(value= > {
expect(value).toBe('observable value');
});
});
});
Copy the code
Services typically depend on other services that Angular injects into constructors. In many cases, these dependencies are easily created and injected manually when the service’s constructor is called.
// ValueService.ts
@Injectable({
providedIn: 'root'
})
export class ValueService {
value = 'real value';
getValue(): string { return this.value; }
setValue(value: string): void { this.value = value; }
getObservableValue(): Observable<string> { return of('observable value'); }
getPromiseValue(): Promise<string> { return Promise.resolve('promise value'); }
getObservableDelayValue(): Observable<string> {
return of('observable delay value').pipe(delay(10)); }}// ValueService.spec.ts
export class FakeValueService extends ValueService {
value = 'faked service value';
}
describe('MasterService without angular testing support'.() = > {
let masterService: MasterService;
it('# getValue returns service'.() = > {
masterService = new MasterService(new ValueService());
expect(masterService.getValue()).toBe('real value');
});
it(Use '# getValue fakeServiece'.() = > {
masterService = new MasterService(new FakeValueService());
expect(masterService.getValue()).toBe('faked service value');
});
it('#getValue use fake object'.() = > {
const fake = { getValue: () = > 'fake value' };
masterService = new MasterService(fake as ValueService);
expect(masterService.getValue()).toBe('fake value');
});
it('#getValue returns by a spy'.() = > {
const valueService: ValueService = new ValueService();
jest.spyOn(valueService, 'getValue').mockReturnValueOnce('test');
masterService = new MasterService(valueService);
expect(masterService.getValue()).toBe('test');
});
});
Copy the code
The first test creates a ValueService using new and passes it to the MasterService constructor. However, injecting real services is difficult to work well because most of the dependent services are difficult to create and control. You can also emulate dependencies, use impersonations, or create a test spy spyOn on the related service method.
2.3 presents TestBed
TestBed is the most important Angular testing utility. TestBed creates a dynamically constructed Angular test module that emulates an Angular @NgModule. TestBed. ConfigureTestingModule () method takes a metadata object, it can have @ NgModule most of the properties. To test a service, you can set an array of services to test or emulate in the metadata property providers.
let masterService: MasterService;
let valueService: ValueService;
beforeEach(() = > {
TestBed.configureTestingModule({
providers: [
MasterService, ValueService
]
});
masterService = TestBed.inject(MasterService);
valueService = TestBed.inject(ValueService);
});
Copy the code
When testing a service with dependencies, use spyOn simultaneously:
// value.service.ts
@Injectable()
export class MasterService {
constructor(private valueService: ValueService) { }
getValue(): string {
return this.valueService.getValue(); }}// value.service.spec.ts
describe('MasterService Dynamic Build '.() = > {
let masterService: MasterService;
let valueService: ValueService;
beforeEach(() = > {
TestBed.configureTestingModule({
providers: [
MasterService, ValueService
]
});
masterService = TestBed.inject(MasterService);
valueService = TestBed.inject(ValueService);
});
it('#getValue returned from spy '.() = > {
const stubValue = 'stub value';
jest.spyOn(valueService, 'getValue').mockReturnValue(stubValue);
expect(masterService.getValue()).toBe(stubValue);
expect(valueService.getValue).toBeCalledTimes(1);
window.console.log(valueService.getValue);
expect(valueService.getValue()).toBe(stubValue);
});
});
Copy the code