Utilização de tecnologias de testes unitários Angular Jasmine. TestBed, que prepara módulo de teste para ambiente de teste, e Spy, que permite a criação de espiões (spies) para simular comportamentos de objetos durante testes. Testes unitários realizados utilizando mock, ambiente específico de dados para testes, evitando testes com dados reais em produção.
export const TODO_STRING: string = `{
"userId": 1,
"id": 15,
"title": "ab voluptatum amet voluptas",
"completed": true
}`;
export const TODOS_STRING: string = `[
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
},
{
"userId": 1,
"id": 2,
"title": "quis ut nam facilis et officia qui",
"completed": false
},
{
"userId": 1,
"id": 3,
"title": "fugiat veniam minus",
"completed": false
},
{
"userId": 1,
"id": 4,
"title": "et porro tempora",
"completed": true
},
{
"userId": 1,
"id": 5,
"title": "laboriosam mollitia et enim quasi adipisci quia provident illum",
"completed": false
}
]`;
export interface Todo {
userId: number;
id: number;
title: string;
completed: boolean;
};
import { TestBed } from '@angular/core/testing';
import { CalculadoraService } from './calculadora.service';
import { LoggerService } from './logger.service';
describe('CalculadoraService', () => {
let service: CalculadoraService;
let loggerSpy: any;
beforeEach(() => { // Executado sempre antes de cada teste (it), afterEach é executado depois de cada teste (it)
loggerSpy = jasmine.createSpyObj('LoggerService', ['log']); // Cria espião para LoggerService
TestBed.configureTestingModule({
providers: [CalculadoraService,
{ provide: LoggerService, useValue: loggerSpy }
], // Configura TestBed para fornecer serviço CalculadoraService
});
service = TestBed.inject(CalculadoraService); // Injeta serviço CalculadoraService para uso nos testes
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('deve somar corretamente dois números', () => {
expect(service).toBeTruthy();
const result = service.calcular(2, 3, 'soma');
expect(result).toBe(5, 'Resultado deve ser 5');
});
it('deve subtrair corretamente dois números', () => {
expect(service).toBeTruthy();
const result = service.calcular(5, 3, 'subtracao');
expect(result).toBe(2, 'Resultado deve ser 2');
});
it('operação não existe', () => {
expect(service).toBeTruthy();
const result = service.calcular(5, 3, 'outro');
expect(result).toBeNull();
expect(loggerSpy.log).toHaveBeenCalledTimes(1); // Verifica se método log foi chamado única vez
});
});
import { Injectable } from '@angular/core';
import { LoggerService } from './logger.service';
@Injectable({
providedIn: 'root'
})
export class CalculadoraService {
constructor(private loggerService: LoggerService) { }
calcular(num1: number, num2: number, operacao: String) {
switch (operacao) {
case 'soma':
return num1 + num2;
case 'subtracao':
return num1 - num2;
case 'divisao':
return num1 / num2;
case 'multiplicacao':
return num1 * num2;
default:
this.loggerService.log(`Operação inválida: ${operacao}`);
// this.loggerService.log(`Operação inválida: ${operacao}`); // Loga operação inválida (chamado 2 vezes no teste)
return null;
}
}
}
import { TestBed } from '@angular/core/testing';
import { LoggerService } from './logger.service';
describe('LoggerService', () => {
let service: LoggerService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(LoggerService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class LoggerService {
constructor() { }
log(msg: string) {
console.log(msg);
}
}
import { TestBed } from '@angular/core/testing';
import { TodosService } from './todos.service';
import { provideHttpClient } from '@angular/common/http';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { environment } from '../../environments/environment.development';
import { TODOS_STRING, TODO_STRING } from '../../../server/db-data';
describe('TodosService', () => {
let todosService: TodosService;
let httpTestingController: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
TodosService,
provideHttpClient(),
provideHttpClientTesting(),
]
});
todosService = TestBed.inject(TodosService);
httpTestingController = TestBed.inject(HttpTestingController);
});
it('should be created', () => {
expect(todosService).toBeTruthy();
});
it('Deve retornar lista TODOS', () => {
/* Método Assíncrono direto - dados reais, não recomendado */
todosService.getAll().subscribe(todos => {
expect(todos).toBeTruthy('Nenhum TODO retornado');
expect(todos.length).toEqual(200, 'Quantidade de TODOs diferente de 200');
const todo = todos.find(todo => todo.id == 15);
expect(todo?.title).toEqual('ab voluptatum amet voluptas');
});
/* Método acima, com Mock (acima id 15 pegará do db-data.ts, e não do http) */
const req = httpTestingController.expectOne(environment.apiUrl + 'todos');
expect(req.request.method).toEqual('GET');
req.flush(JSON.parse(TODOS_STRING));
});
it('Deve retornar TODO por Id', () => {
/* Método Assíncrono direto - dados reais, não recomendado */
todosService.getById(15).subscribe(todo => {
expect(todo).toBeTruthy();
expect(todo.id).toEqual(15);
});
/* Método acima, com Mock (acima id 15 pegará do db-data.ts, e não do http) */
const req = httpTestingController.expectOne(environment.apiUrl + 'todos/15');
expect(req.request.method).toEqual('GET');
req.flush(JSON.parse(TODO_STRING));
});
/* Método Assíncrono acima, com Promise - dados reais, não recomendado
it('Deve retornar TODO por Id', async () => {
const todo = await todosService.getById(15).toPromise();
expect(todo).toBeTruthy();
expect(todo?.id).toEqual(15);
});
*/
});
import { Injectable } from '@angular/core';
import { environment } from '../../environments/environment.development';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';
import { Todo } from '../_models/Todo';
@Injectable({
providedIn: 'root'
})
export class TodosService {
baseUrl: string = environment.apiUrl;
constructor(private http: HttpClient) { }
getAll() {
return this.http.get<Todo[]>(this.baseUrl + 'todos')
.pipe(
map((response) => {
return response;
})
);
}
getById(id: number) {
return this.http.get<Todo>(this.baseUrl + 'todos/' + id)
.pipe(
map((response) => {
return response;
})
);
}
}
import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient(),
]
};
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { OnInit } from '@angular/core';
import { TodosService } from './_services/todos.service';
@Component({
selector: 'app-root',
imports: [RouterOutlet],
templateUrl: './app.html',
styleUrl: './app.css'
})
export class App {
protected title = 'teste_unitario_aula';
}
export const environment = {
apiUrl: 'http://jsonplaceholder.typicode.com/',
};
export const environment = {
apiUrl: 'http://jsonplaceholder.typicode.com/',
};
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { App } from './app/app';
bootstrapApplication(App, appConfig)
.catch((err) => console.error(err));
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"include": [
"src/**/*.ts"
],
"exclude": [
"src/**/*.spec.ts"
]
}
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine"
]
},
"include": [
"src/**/*.ts"
]
}
Elaborado por Mateus Schwede
ubsocial.github.io