Angular component unit tests not only examine logic but also assess the values that will be presented on the screen.
If your application follows the component architecture recommended by the Angular team (more details in Chapter 4, Components and Pages), you probably won’t have much business logic in your components, delegating it to services.
To exemplify, in this section, we will create tests for some methods of the DiaryComponent component.
We will create the test case for the gym diary entry deletion operation and check whether the service’s delete method is called:
describe(‘DiaryComponent’, () => {
let exerciseSetsService: ExerciseSetsService;
beforeEach(async () => {
await TestBed.configureTestingModule({
}).compileComponents();
exerciseSetsService = TestBed.inject(ExerciseSetsService);
});
it(‘should call delete method when the button delete is clicked’, fakeAsync(() => {
exerciseSetsService.deleteItem = jasmine.createSpy().and.returnValue(of());
component.deleteItem(‘1’);
tick();
expect(exerciseSetsService.deleteItem).toHaveBeenCalledOnceWith(‘1’);
}));
});
In the preceding code block, we are testing the DiaryComponent component, so we mock the service it depends on with TestBed. But for this test, we need a reference to this service, and for that, we declare a variable called exerciseSetsService. With the TestBed.inject method, we assign the value to this variable.
In the test setup, we need to use the createSpy function to assign the service’s deleteItem method because the mock generated by the Jasmine framework does not have the full implementation of the service and therefore does not return the observable that the component is expecting.
In the execution phase, we call the deleteItem method of the component.
As this operation is asynchronous, we use the tick function to simulate the passage of time.
In the assertion phase, we check that the exerciseSetsService service method was called once and with the expected parameter.
Let’s test the editEntry method next:
import { Location } from ‘@angular/common’;
describe(‘DiaryComponent’, () => {
let location: Location;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes([
{
path: ‘home/diary/entry/:id’,
component: NewEntryFormReactiveComponent, },
]),
]
}).compileComponents();
location = TestBed.inject(Location);
});
it(‘should direct to diary entry edit route’, fakeAsync(() => {
const set: ExerciseSet = { date: new Date(), exercise: ‘test’, reps: 6, sets: 6, id: ‘1’ };
component.editEntry(set);
tick();
expect(location.path()).toBe(‘/home/diary/entry/1’);
}));
});
To perform the assertion of the route, we are going to use an object of type Location – that’s why we declare it at the beginning of the test and assign it using the TestBed component. Note that we want the @angular/common library object and not the browser’s default Location object. Also, in TestBed, we need to declare a route, because as we are in the context of unit testing, Angular does not know the routes available for use.
In the test case, we first create a dummy ExerciseSet object and call the editEntry method. Again, we use the tick function to simulate the passage of time. Finally, in the assertion, we verify that the path is correct. Note that, here, we don’t need to create any mock for the router as the RouterTestingModule module creates it for us.
In the next section, we will explore E2E testing with the Cypress framework.
No Responses