Elioenai Garcia
EstudianteVanessa Paola Oliveros Padron
EstudianteJosé Luis Jiménez
EstudianteLuis Jose Marquez Gonzalez
EstudianteCesar Elías Armendariz Ruano
EstudianteLuis Antonio Gutiérrez Hernández
EstudianteJosé Luis Jiménez
EstudianteCristian Danilo Motta Herrera
Estudiantesrc/testing/activated-route-stub.ts
import { convertToParamMap, ParamMap, Params } from '@angular/router'; import { ReplaySubject } from 'rxjs'; /** * An ActivateRoute test double with a `paramMap` observable. * Use the `setParamMap()` method to add the next `paramMap` value. */ export class ActivatedRouteStub { // Use a ReplaySubject to share previous values with subscribers // and pump new values into the `paramMap` observable private subject = new ReplaySubject<ParamMap>(); constructor(initialParams?: Params) { this.setParamMap(initialParams); } /** The mock paramMap observable */ readonly paramMap = this.subject.asObservable(); /** Set the paramMap observable's next value */ setParamMap(params: Params = {}) { this.subject.next(convertToParamMap(params)); } }
Actualmente 2024 como se prueban los ActivatedRoute ya que en la documentacion no veo el "Stub"?
Por lo que veo ya no se suele usar el "Stub" sino una nueva estrategia llamada RouterTestingHarness..
Puedes utilizar el RouterTestingHarness. Dejo un ejemplo:
import { provideLocationMocks } from '@angular/common/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { provideRouter } from '@angular/router'; import { RouterTestingHarness } from '@angular/router/testing'; import { mockObservable } from '../../../testing'; import { generateOneProduct } from '../../models/product.mock'; import { ProductsService } from '../../services/product.service'; import { ProductDetailComponent } from './product-detail.component'; fdescribe('ProductDetailComponent', () => { let component: ProductDetailComponent; let fixture: ComponentFixture<ProductDetailComponent>; let productService: jasmine.SpyObj<ProductsService>; let location: jasmine.SpyObj<Location>; beforeEach(async () => { const productServiceSpy = jasmine.createSpyObj('ProductsService', ['getOne']) const locationSpy = jasmine.createSpyObj('Location', ['back']); await TestBed.configureTestingModule({ imports: [ ProductDetailComponent ], providers: [ provideRouter([ { path: 'products/:id', component: ProductDetailComponent, }, ]), provideLocationMocks(), { provide: ProductsService, useValue: productServiceSpy }, { provide: Location, useValue: locationSpy } ] }) .compileComponents(); }); beforeEach(async() => { fixture = TestBed.createComponent(ProductDetailComponent); const productId = '1'; const harness = await RouterTestingHarness.create(); component = await harness.navigateByUrl(`products/${productId}`, ProductDetailComponent); productService = TestBed.inject(ProductsService) as unknown as jasmine.SpyObj<ProductsService>; location = TestBed.inject(Location) as unknown as jasmine.SpyObj<Location>; const productMock = {...generateOneProduct(), id: productId}; productService.getOne.and.returnValue(mockObservable(productMock)); fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); });
Vamos a hacer las pruebas que estan siendo enrutados y reciben ciertos parametros,
product-detail.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute } from '@angular/router'; import { Location } from '@angular/common'; import { ActivatedRouteStub, mockObservable } from './../../../../testing'; import { ProductsService } from '../../../services/product.service'; import { generateOneProduct } from '../../../models/product.mock'; import { ProductDetailComponent } from './product-detail.component'; describe('ProductDetailComponent', () => { let component: ProductDetailComponent; let fixture: ComponentFixture<ProductDetailComponent>; let route: ActivatedRouteStub; let productsService: jasmine.SpyObj<ProductsService>; let location: jasmine.SpyObj<Location>; beforeEach(async () => { const routeStub = new ActivatedRouteStub(); const productServiceSpy = jasmine.createSpyObj('ProductsService', ['getOne']); const locationSpy = jasmine.createSpyObj('Location', ['back']); await TestBed.configureTestingModule({ declarations: [ ProductDetailComponent ], providers: [ { provide: ActivatedRoute, useValue: routeStub }, { provide: ProductsService, useValue: productServiceSpy }, { provide: Location, useValue: locationSpy }, ], }) .compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(ProductDetailComponent); component = fixture.componentInstance; route = TestBed.inject(ActivatedRoute) as unknown as ActivatedRouteStub; productsService = TestBed.inject(ProductsService) as unknown as jasmine.SpyObj<ProductsService>; location = TestBed.inject(Location) as unknown as jasmine.SpyObj<Location>; const productId = '1'; route.setParamMap({id: productId}); const productMock = { ...generateOneProduct(), id: productId, } productsService.getOne.and.returnValue(mockObservable(productMock)); fixture.detectChanges(); // ngOnInit }); it('should create', () => { expect(component).toBeTruthy(); }); });
¿Cuál sería la diferencia entre usar useValue y useClass?
Una lección importante que aprendí al realizar pruebas en Angular es que, en muchos casos, es necesario configurar datos antes de que se ejecute el método ngOnInit o incluso el constructor de un componente. Por este motivo, prefiero usar un paradigma basado en una función de configuración (setup) en lugar de los métodos tradicionales como beforeAll.
El enfoque de setup permite definir una configuración por defecto que se ejecuta al iniciar cada prueba. Si necesitas personalizar algún parámetro, puedes hacerlo pasando los valores requeridos a la función, la cual se encargará de realizar el mock de los datos antes de ejecutar la prueba.
A continuación, presento un ejemplo que ilustra esta idea. La función _setup incluye una configuración por defecto, pero también admite parámetros específicos cuando es necesario. Este enfoque asegura que cada prueba inicie con los datos adecuados, reduciendo el riesgo de problemas derivados de configuraciones incompletas o inconsistentes.
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProductDetailComponent } from './product-detail.component';
import { ActivatedRouteStub, mockObservable } from './../../../../testing';
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
import { ProductsService } from '../../../services/product.service';
import { generateOneProduct } from '../../../models/product.mock';
import { Product } from 'src/app/models/product.model';
fdescribe('ProductDetailComponent', () => {
async function _setup(data?: { mockProduct?: Product }) {
if (!data) data = {};
const { mockProduct = generateOneProduct() } = data;
const _activatedRouterSpy = new ActivatedRouteStub({ id: mockProduct.id });
const _productsServiceSpy = jasmine.createSpyObj('ProductsService', ['getOne']);
const _locationSpy = jasmine.createSpyObj('Location', ['back']);
await TestBed.configureTestingModule({
declarations: [ProductDetailComponent],
providers: [
{ provide: ActivatedRoute, useValue: _activatedRouterSpy },
{ provide: Location, useValue: _locationSpy },
{ provide: ProductsService, useValue: _productsServiceSpy }
]
}).compileComponents();
const fixture = TestBed.createComponent(ProductDetailComponent);
const component = fixture.componentInstance;
const activatedRouterSpy = TestBed.inject(ActivatedRoute) as jasmine.SpyObj<ActivatedRoute>;
const productsServiceSpy = TestBed.inject(ProductsService) as jasmine.SpyObj<ProductsService>;
const locationSpy = TestBed.inject(Location) as jasmine.SpyObj<Location>;
productsServiceSpy.getOne.and.returnValue(mockObservable(mockProduct));
return { fixture, component, activatedRouterSpy, productsServiceSpy, locationSpy };
}
it('should create', async () => {
const { component, fixture } = await _setup();
fixture.detectChanges();
expect(component).toBeTruthy();
});
});