Outcome Recipes
Advanced testing patterns for complex scenarios including async operations, routing, service mocking, and end-to-end component interaction testing.
1. Testing components with async API calls
Steps
import { IHttpClient } from '@aurelia/fetch-client'; import { Registration } from '@aurelia/kernel'; export class MockHttpClient implements IHttpClient { private mockResponses = new Map<string, any>(); private mockErrors = new Map<string, Error>(); baseUrl = ''; activeRequestCount = 0; isRequesting = false; configure() { return this; } setMockResponse(url: string, data: any, status: number = 200) { this.mockResponses.set(url, { data, status }); } setMockError(url: string, error: Error) { this.mockErrors.set(url, error); } async fetch(input: RequestInfo | Request): Promise<Response> { const url = typeof input === 'string' ? input : input.url; if (this.mockErrors.has(url)) { throw this.mockErrors.get(url); } const mock = this.mockResponses.get(url); if (!mock) { throw new Error(`No mock response configured for ${url}`); } return new Response(JSON.stringify(mock.data), { status: mock.status, headers: { 'Content-Type': 'application/json' } }); } } export const MockHttpClientRegistration = Registration.instance( IHttpClient, new MockHttpClient() );import { createFixture } from '@aurelia/testing'; import { MockHttpClient, MockHttpClientRegistration } from './mock-http-client'; import { ProductList } from './product-list'; describe('ProductList', () => { it('should load and display products', async () => { const mockHttp = new MockHttpClient(); mockHttp.setMockResponse('/api/products', { products: [ { id: '1', name: 'Product 1', price: 10 }, { id: '2', name: 'Product 2', price: 20 } ] }); const { component, assertText, platform } = await createFixture .component(ProductList) .html`<div> <div if.bind="loading">Loading...</div> <div if.bind="error">\${error}</div> <div repeat.for="product of products">\${product.name}</div> </div>` .deps(Registration.instance(IHttpClient, mockHttp)) .build() .started; // Initial loading state assertText('Loading...'); // Wait for async attached() to complete await tasksSettled(); // Verify products are displayed assertText('Product 1Product 2'); expect(component.loading).toBe(false); expect(component.products.length).toBe(2); await fixture.stop(true); }); });it('should display error message when API fails', async () => { const mockHttp = new MockHttpClient(); mockHttp.setMockError('/api/products', new Error('Network error')); const { component, assertText, platform } = await createFixture .component(ProductList) .html`<div> <div if.bind="loading">Loading...</div> <div if.bind="error">\${error}</div> <div repeat.for="product of products">\${product.name}</div> </div>` .deps(Registration.instance(IHttpClient, mockHttp)) .build() .started; // Wait for async operation await tasksSettled(); // Verify error is displayed expect(component.error).toBeTruthy(); expect(component.products.length).toBe(0); expect(component.loading).toBe(false); await fixture.stop(true); });it('should retry loading when retry button is clicked', async () => { const mockHttp = new MockHttpClient(); let callCount = 0; // First call fails, second succeeds mockHttp.fetch = async (input: RequestInfo | Request) => { callCount++; if (callCount === 1) { throw new Error('Temporary error'); } return new Response(JSON.stringify({ products: [{ id: '1', name: 'Product 1', price: 10 }] }), { status: 200, headers: { 'Content-Type': 'application/json' } }); }; const { component, trigger, assertText, platform } = await createFixture .component(ProductList) .html`<div> <div if.bind="loading">Loading...</div> <div if.bind="error"> \${error} <button click.trigger="retry()">Retry</button> </div> <div repeat.for="product of products">\${product.name}</div> </div>` .deps(Registration.instance(IHttpClient, mockHttp)) .build() .started; // Wait for initial failed load await tasksSettled(); expect(component.error).toBeTruthy(); // Click retry button trigger.click('button'); // Wait for retry to complete await tasksSettled(); // Verify success assertText('Product 1'); expect(component.error).toBeNull(); expect(callCount).toBe(2); await fixture.stop(true); });
Checklist
2. Testing router navigation and route parameters
Steps
Checklist
3. Testing with validation
Steps
Checklist
4. Testing complex component interactions
Steps
Checklist
5. Testing lifecycle hooks in complex scenarios
Steps
Checklist
6. Testing with real-world dependencies
Steps
Checklist
Testing pattern cheat sheet
Scenario
Key Approach
Tools/APIs
Best practices
See also
Last updated
Was this helpful?