Паттерн "Построение - Операции - Проверка"
Паттерн "Построение - Операции - Проверка" (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):
Примеры демонстрируют, как можно использовать паттерн "Построение - Операции - Проверка" для написания тестов. Такой подход помогает структурировать тесты, делая их более понятными и легкими в поддержке.