Overview
Angular Bases uses Vitest as its testing framework. Vitest provides a fast, modern testing experience with excellent TypeScript support and familiar Jest-compatible APIs.
Running Tests
Execute Test Suite
This runs all test files matching the pattern *.spec.ts.
Test Configuration
TypeScript Configuration
Test files use tsconfig.spec.json which extends the base configuration:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"vitest/globals"
]
},
"include": [
"src/**/*.d.ts",
"src/**/*.spec.ts"
]
}
Key features:
- Vitest globals - Access to
describe, it, expect without imports
- Type definitions - Full TypeScript support for Vitest APIs
- Test file inclusion - All
.spec.ts files are included
Angular Testing Configuration
The Angular CLI uses the @angular/build:unit-test builder configured in angular.json:
{
"test": {
"builder": "@angular/build:unit-test"
}
}
Writing Tests
Test File Structure
Test files should be named with the .spec.ts extension and typically placed alongside the component they test:
src/app/pages/counter/
├── counter-page.ts
├── counter-page.html
└── counter-page.spec.ts
Basic Test Example
import { TestBed } from '@angular/core/testing';
import { CounterPage } from './counter-page';
describe('CounterPage', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CounterPage]
}).compileComponents();
});
it('should create', () => {
const fixture = TestBed.createComponent(CounterPage);
const component = fixture.componentInstance;
expect(component).toBeTruthy();
});
it('should increment counter', () => {
const fixture = TestBed.createComponent(CounterPage);
const component = fixture.componentInstance;
const initialCount = component.counter();
component.increment();
expect(component.counter()).toBe(initialCount + 1);
});
});
Testing Patterns
Component Testing
Configure TestBed
Set up the testing module with required imports:await TestBed.configureTestingModule({
imports: [MyComponent]
}).compileComponents();
Create Component Fixture
const fixture = TestBed.createComponent(MyComponent);
const component = fixture.componentInstance;
Test Component Behavior
component.someMethod();
fixture.detectChanges();
expect(component.someProperty).toBe(expectedValue);
Query DOM Elements
const compiled = fixture.nativeElement;
const button = compiled.querySelector('button');
expect(button.textContent).toContain('Click me');
Testing with Signals
Angular Bases uses signals for reactive state. Test them like this:
it('should update signal value', () => {
const fixture = TestBed.createComponent(MyComponent);
const component = fixture.componentInstance;
// Read signal value
expect(component.mySignal()).toBe(initialValue);
// Update signal
component.mySignal.set(newValue);
// Verify change
expect(component.mySignal()).toBe(newValue);
});
Testing Router Components
For components using routing:
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MyComponent],
providers: [
provideRouter(routes)
]
}).compileComponents();
});
Testing Services
import { TestBed } from '@angular/core/testing';
import { MyService } from './my.service';
describe('MyService', () => {
let service: MyService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(MyService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('should return expected data', () => {
const result = service.getData();
expect(result).toEqual(expectedData);
});
});
Vitest Features
Watch Mode
Vitest runs in watch mode by default, re-running tests when files change:
Press a to run all tests, or r to rerun tests.
Test Filtering
Run specific tests:
npm test -- counter-page.spec.ts
Or use .only to focus on specific tests:
it.only('should run only this test', () => {
// Test code
});
Coverage Reports
Generate code coverage reports:
You may need to install @vitest/coverage-v8 or @vitest/coverage-istanbul for coverage reporting:npm install -D @vitest/coverage-v8
Debugging Tests
Run tests with the Node debugger:
node --inspect-brk ./node_modules/vitest/vitest.mjs
Or add debugger statements in your test code and use browser debugging tools.
Best Practices
Test Organization
- One test file per component - Keep tests alongside their components
- Descriptive test names - Use clear descriptions of what’s being tested
- Arrange-Act-Assert - Structure tests with setup, action, and verification
it('should display counter value when incremented', () => {
// Arrange
const fixture = TestBed.createComponent(CounterPage);
const compiled = fixture.nativeElement;
// Act
fixture.componentInstance.increment();
fixture.detectChanges();
// Assert
const counterDisplay = compiled.querySelector('.counter');
expect(counterDisplay.textContent).toContain('1');
});
Component Testing
- Test user interactions - Simulate clicks, inputs, and other events
- Test component outputs - Verify events are emitted correctly
- Test DOM rendering - Ensure templates render expected content
- Isolate dependencies - Use mocks and stubs for external dependencies
Coverage Goals
Aim for:
- Statements: 80%+
- Branches: 75%+
- Functions: 80%+
- Lines: 80%+
Focus on testing critical paths and business logic rather than achieving 100% coverage.
Testing with JSDOM
Angular Bases includes jsdom for DOM simulation in tests:
"devDependencies": {
"jsdom": "^27.1.0"
}
This allows testing DOM interactions without a real browser.
Common Testing Scenarios
it('should validate form input', () => {
const fixture = TestBed.createComponent(FormComponent);
const compiled = fixture.nativeElement;
const input = compiled.querySelector('input');
input.value = '[email protected]';
input.dispatchEvent(new Event('input'));
fixture.detectChanges();
expect(fixture.componentInstance.form.valid).toBe(true);
});
Testing HTTP Calls
import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting, HttpTestingController } from '@angular/common/http/testing';
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
provideHttpClient(),
provideHttpClientTesting()
]
});
});
it('should fetch data', () => {
const httpTesting = TestBed.inject(HttpTestingController);
const service = TestBed.inject(DataService);
service.getData().subscribe(data => {
expect(data).toEqual(mockData);
});
const req = httpTesting.expectOne('/api/data');
expect(req.request.method).toBe('GET');
req.flush(mockData);
});
Testing Async Operations
import { fakeAsync, tick } from '@angular/core/testing';
it('should handle async operations', fakeAsync(() => {
const fixture = TestBed.createComponent(AsyncComponent);
const component = fixture.componentInstance;
component.loadData();
tick(1000); // Simulate time passing
expect(component.data()).toBeDefined();
}));
Troubleshooting
Tests Not Running
Check:
- Test files have
.spec.ts extension
tsconfig.spec.json includes your test files
- Dependencies are installed (
npm install)
Import Errors
Ensure:
vitest/globals is in tsconfig.spec.json types
- Angular testing utilities are imported from
@angular/core/testing
Component Not Found
Verify:
- Component is imported in TestBed configuration
- Component is standalone or declared in a testing module
Next Steps