Skip to main content

Unit Testing Cookbook

[🚧 This file is under construction]

Test Structure​

describe('$className$', () => {
beforeAll(() => {
$customElements$
});

beforeEach(async () => {
$subjectImplementations$

await TestBed.configureTestingModule({
imports: [
$components$,
$pipes$,
$directives$
],
providers: [
$providedInRootServices$,
$routerServices$
],
declarations: [
$mockedComponents$
]
});
});
});

HTML/DOM Testing​

  • Use DebugElement instead of native query selectors.
  • Benefits:
    1. Platform independent abstraction
    2. Event simulation
    3. Cross-platform support

Angular DebugElement Docs

Method Call Testing​

it('should spy on method', () => {
const user = { activate: jest.fn() };
const spy = jest.spyOn(user, 'activate');

user.activate();

expect(spy).toHaveBeenCalled();
});

The Jest Object

Data Provider Testing with it.each​

function sum(a: number, b: number): number {
return a + b;
}

describe('sum function with it.each', () => {
it.each([
[0, 1, 1],
[5, 3, 8],
[-2, 4, 2],
[10, -5, 5],
[0, 100, 100],
])(
'should return the correct sum for %s + %s = %s',
(a, b, expected) => {
expect(sum(a, b)).toBe(expected);
}
);
});

More info

Updating Component Inputs​

describe('OverviewCardComponent', () => {
let component: OverviewCardComponent;
let fixture: ComponentFixture<OverviewCardComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MyComponent],
}).compileComponents();

fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
});

it('should update input', () => {
fixture.componentRef.setInput('myInput', false);
fixture.detectChanges();
});
});

Globals Testing​

Console Error Mocking​

it('should handle exceptions in the log.', async () => {
const errorLogSpy = jest
.spyOn(global.console, 'error')
.mockImplementation(jest.fn());

console.error('my error')

expect(errorLogSpy).toHaveBeenCalled();
});

...

TODO​

  • Add templates for fast implementation
  • Improve ESLint compatibility for max-classes-per-file

Note: All interactions are done using data-test properties, not CSS class selectors.