Паттерн "Построение - Операции - Проверка"
Паттерн "Построение - Операции - Проверка" (Arrange - Act - Assert, AAA) широко используется при написании тестов для организации тестового кода и повышения его читаемости. Этот паттерн делит тест на три четко определенные секции:
- Построение (Arrange): Подготовка всего необходимого для теста, например, создание объектов, настройка условий.
- Операции (Act): Выполнение действий, которые нужно протестировать.
- Проверка (Assert): Проверка, что результаты выполнения действий соответствуют ожидаемым значениям.
Рассмотрим пример использования этого паттерна при написании теста для Angular компонента.
Предположим, у нас есть простой Angular компонент, который выполняет некую операцию. Например, компонент CalculatorComponent
, который имеет метод для сложения двух чисел.
// calculator.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-calculator', template: ` <div> <input [(ngModel)]="a" type="number" placeholder="Enter first number"> <input [(ngModel)]="b" type="number" placeholder="Enter second number"> <button (click)="add()">Add</button> <p>Result: {{ result }}</p> </div> ` }) export class CalculatorComponent { a: number = 0; b: number = 0; result: number = 0; add() { this.result = this.a + this.b; } }
Тест
Теперь напишем тест для компонента CalculatorComponent
, который проверяет корректность работы метода add
.
// calculator.component.spec.ts import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; import { CalculatorComponent } from './calculator.component'; describe('CalculatorComponent', () => { let component: CalculatorComponent; let fixture: ComponentFixture<CalculatorComponent>; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ CalculatorComponent ], imports: [ FormsModule ] }) .compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(CalculatorComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should add two numbers correctly', () => { // Arrange component.a = 5; component.b = 3; // Act component.add(); // Assert expect(component.result).toBe(8); }); });
Пояснение к тесту
- Построение (Arrange):
- Создаем экземпляр компонента
CalculatorComponent
с помощьюTestBed
. - Устанавливаем значения для свойств
a
иb
. - Операции (Act):
- Проверка (Assert):
Рассмотрим другой пример, на этот раз с компонентом, который делает HTTP-запрос для получения данных. Мы напишем тест для проверки корректности обработки этих данных.
Создание Angular компонента с HTTP-запросом
Предположим, у нас есть компонент UserComponent
, который получает список пользователей с сервера.
// user.component.ts import { Component, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; interface User { id: number; name: string; } @Component({ selector: 'app-user', template: ` <div *ngIf="users"> <ul> <li *ngFor="let user of users">{{ user.name }}</li> </ul> </div> ` }) export class UserComponent implements OnInit { users: User[] | undefined; constructor(private http: HttpClient) {} ngOnInit() { this.getUsers().subscribe(users => this.users = users); } getUsers(): Observable<User[]> { return this.http.get<User[]>('https://jsonplaceholder.typicode.com/users'); } }
Тест
Теперь напишем тест для компонента UserComponent
, который проверяет корректность получения и отображения списка пользователей.
// user.component.spec.ts import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { TestBed, ComponentFixture } from '@angular/core/testing'; import { UserComponent } from './user.component'; import { HttpClient } from '@angular/common/http'; import { DebugElement } from '@angular/core'; import { By } from '@angular/platform-browser'; describe('UserComponent', () => { let component: UserComponent; let fixture: ComponentFixture<UserComponent>; let httpMock: HttpTestingController; let httpClient: HttpClient; let debugElement: DebugElement; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ UserComponent ], imports: [ HttpClientTestingModule ] }) .compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(UserComponent); component = fixture.componentInstance; httpMock = TestBed.inject(HttpTestingController); httpClient = TestBed.inject(HttpClient); debugElement = fixture.debugElement; fixture.detectChanges(); }); it('should fetch and display a list of users', () => { // Arrange const mockUsers = [ { id: 1, name: 'John Doe' }, { id: 2, name: 'Jane Doe' } ]; // Act const request = httpMock.expectOne('https://jsonplaceholder.typicode.com/users'); request.flush(mockUsers); fixture.detectChanges(); // Assert const userElements = debugElement.queryAll(By.css('li')); expect(userElements.length).toBe(2); expect(userElements[0].nativeElement.textContent).toContain('John Doe'); expect(userElements[1].nativeElement.textContent).toContain('Jane Doe'); }); afterEach(() => { httpMock.verify(); }); });
Пояснение к тесту
- Построение (Arrange):
- Создаем экземпляр компонента
UserComponent
с помощьюTestBed
. - Устанавливаем
HttpClientTestingModule
для подмены HTTP-запросов. - Определяем макет данных (
mockUsers
), который будет возвращен при HTTP-запросе. - Операции (Act):
- Запускаем HTTP-запрос и подменяем ответ с помощью метода
flush
. - Вызываем
fixture.detectChanges()
для обновления шаблона компонента. - Проверка (Assert):
Примеры демонстрируют, как можно использовать паттерн "Построение - Операции - Проверка" для написания тестов. Такой подход помогает структурировать тесты, делая их более понятными и легкими в поддержке.