Angular Component Testing
Test the contract a user or consumer relies on, not merely the existence of a class. Angular’s TestBed creates a component with its providers and template so a test can exercise real bindings.
Your brief
The desk counter is intentionally small enough to inspect. Its supplied app.spec.ts checks the first rendered state, clicks the actual button and waits for Angular to settle before checking the changed text. In a zoneless application, use supported notifications and wait for stability rather than assuming arbitrary timers make a test correct. The course also runs the compiled app in a browser, because a DOM emulator does not prove every browser behavior.
Run the code
Use a separate empty project folder with a compatible Node version: ^22.22.3, ^24.15.0 or a supported 26+ runtime. The review uses Node 24.22.1, Angular 22.2.4, CLI/build 22.2.5 and TypeScript 6.0.3. Node 22.15.1 is too old for Angular 22. Save every download at its displayed path; browsers may keep only the basename, so recreate the src folder. Run npm install, npm run build and npm start, then open the loopback address printed by the server. Run npm test for the supplied component tests. These are local teaching projects, not Bhylu publishing files.
The small loopback server serves the compiled app and fixed, read-only tool API fixtures. It is not a database, authentication or lending service. Angular examples do not run in Bhylu’s current HTML or TypeScript playground. No Apache, MySQL or PHP configuration change is needed.
Example
Show the worked solution
import { Component } from '@angular/core';
import { signal } from '@angular/core';
@Component({
selector: 'bh-testing',
standalone: true,
imports: [],
template: `<button type="button" (click)="count.update(add)">Record return</button>
<p data-result>{{ count() }} returns</p>`,
})
export class App {
count = signal(0);
add = (value: number) => value + 1;
}
Expected result
0 returns
The worked component and the downloaded test are both compiled. The real test runner uses Vitest through Angular’s application tooling; it is not a screenshot labeled as a unit test. HTTP, routing, invalid input and destruction each need their own meaningful assertions as the component’s responsibility grows.
Supporting source
import { describe, it, expect } from 'vitest';
import { TestBed } from '@angular/core/testing';
import { provideZonelessChangeDetection } from '@angular/core';
import { App } from './app';
import { appConfig } from './app.config';
describe('Component Testing', () => {
it('renders the initial contract and handles the documented operations', async () => {
TestBed.configureTestingModule({
imports: [App],
providers: [...(appConfig.providers ?? [])],
});
const fixture = TestBed.createComponent(App);
const element = fixture.nativeElement as HTMLElement;
fixture.detectChanges();
await fixture.whenStable();
const text = () => element.querySelector('[data-result]')?.textContent?.trim();
expect(text()).toBe('0 returns');
element.querySelector<HTMLElement>('button')!.click();
fixture.detectChanges();
await fixture.whenStable();
expect(text()).toBe('1 returns');
fixture.destroy();
});
});