Skip to main content

Overview

Count App uses Vitest as its testing framework, configured through Angular’s built-in test builder. Vitest provides a fast, modern testing experience with features like:
  • Fast execution with smart watch mode
  • ESM-first design
  • Compatible with Jest’s API
  • Built-in TypeScript support

Running Tests

Run all tests using the npm test script:
npm test
Or using the Angular CLI directly:
ng test
The test command uses the @angular/build:unit-test builder configured in angular.json.

Test Configuration

TypeScript Configuration

The test setup is configured in tsconfig.spec.json:
tsconfig.spec.json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "./out-tsc/spec",
    "types": [
      "vitest/globals"
    ]
  },
  "include": [
    "src/**/*.d.ts",
    "src/**/*.spec.ts"
  ]
}
Key settings:
  • Extends the base TypeScript configuration
  • Includes Vitest global types (describe, it, expect, etc.)
  • Only includes .spec.ts and .d.ts files from the src directory

Angular Test Builder

The test builder is configured in angular.json:
angular.json
"test": {
  "builder": "@angular/build:unit-test"
}

Writing Tests

Basic Component Test

Here’s the example test from src/app/app.spec.ts:
src/app/app.spec.ts
import { TestBed } from '@angular/core/testing';
import { App } from './app';

describe('App', () => {
  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [App],
    }).compileComponents();
  });

  it('should create the app', () => {
    const fixture = TestBed.createComponent(App);
    const app = fixture.componentInstance;
    expect(app).toBeTruthy();
  });

  it('should render title', async () => {
    const fixture = TestBed.createComponent(App);
    await fixture.whenStable();
    const compiled = fixture.nativeElement as HTMLElement;
    expect(compiled.querySelector('h1')?.textContent).toContain('Hello, count-app');
  });
});

Test Structure

1

Set up TestBed

Use TestBed.configureTestingModule() to configure the testing module with the component and its dependencies:
beforeEach(async () => {
  await TestBed.configureTestingModule({
    imports: [App],
  }).compileComponents();
});
2

Create component fixture

Create a fixture to interact with the component:
const fixture = TestBed.createComponent(App);
const app = fixture.componentInstance;
3

Write assertions

Use Vitest’s expect API to make assertions:
expect(app).toBeTruthy();
expect(compiled.querySelector('h1')?.textContent)
  .toContain('Hello, count-app');

Test Patterns

Testing Component Creation

it('should create the app', () => {
  const fixture = TestBed.createComponent(App);
  const app = fixture.componentInstance;
  expect(app).toBeTruthy();
});

Testing Template Rendering

it('should render title', async () => {
  const fixture = TestBed.createComponent(App);
  await fixture.whenStable();
  const compiled = fixture.nativeElement as HTMLElement;
  expect(compiled.querySelector('h1')?.textContent)
    .toContain('Hello, count-app');
});
Always call await fixture.whenStable() when testing asynchronous operations or template bindings to ensure all changes are applied.

Testing User Interactions

For testing button clicks and counter updates:
it('should increment counter when button is clicked', async () => {
  const fixture = TestBed.createComponent(App);
  await fixture.whenStable();
  
  const button = fixture.nativeElement.querySelector('button');
  button?.click();
  fixture.detectChanges();
  
  const counter = fixture.nativeElement.querySelector('.counter');
  expect(counter?.textContent).toContain('1');
});

Best Practices

Group related tests together:
describe('CounterComponent', () => {
  describe('increment()', () => {
    it('should increase count by 1', () => {
      // test
    });
  });
  
  describe('decrement()', () => {
    it('should decrease count by 1', () => {
      // test
    });
  });
});
Use beforeEach and afterEach for setup and cleanup:
let fixture: ComponentFixture<App>;
let component: App;

beforeEach(async () => {
  await TestBed.configureTestingModule({
    imports: [App],
  }).compileComponents();
  
  fixture = TestBed.createComponent(App);
  component = fixture.componentInstance;
});
Focus on testing what the component does, not how it does it:
// Good: Tests user-visible behavior
it('should display the current count', () => {
  expect(compiled.textContent).toContain('Count: 0');
});

// Avoid: Tests implementation details
it('should have a count property', () => {
  expect(component.count).toBeDefined();
});

VSCode Integration

The project includes a VSCode launch configuration for running tests (see .vscode/launch.json):
{
  "name": "ng test",
  "type": "chrome",
  "request": "launch",
  "preLaunchTask": "npm: test",
  "url": "http://localhost:9876/debug.html"
}
To use it:
  1. Open the Run and Debug panel (Ctrl+Shift+D)
  2. Select “ng test” from the dropdown
  3. Press F5 or click the play button

Troubleshooting

Ensure your tsconfig.spec.json includes the correct files:
"include": [
  "src/**/*.d.ts",
  "src/**/*.spec.ts"
]
Make sure vitest/globals is in your types array in tsconfig.spec.json:
"types": [
  "vitest/globals"
]
Call fixture.detectChanges() to trigger change detection:
button.click();
fixture.detectChanges(); // Update the view

Next Steps

Building for Production

Learn how to build and deploy your application

Build docs developers (and LLMs) love