Overview
Karma Ecommerce uses a robust testing setup with:
Karma (v6.4.0) - Test runner
Jasmine (v5.9.0) - Testing framework
Angular Testing Utilities - Component and service testing tools
Coverage Reports - Track test coverage
Running Tests
To run the unit tests:
This command:
Launches Karma test runner
Opens Chrome browser in watch mode
Runs all *.spec.ts test files
Watches for changes and re-runs tests automatically
By default, tests run in watch mode. The browser will stay open and tests will re-run when you save changes to test files or source files.
Single Run Mode
For CI/CD pipelines or one-time test execution:
ng test --watch=false --browsers=ChromeHeadless
With Coverage
To generate coverage reports:
Coverage reports are generated in the coverage/ directory. Open coverage/index.html in a browser to view detailed coverage information.
Test Structure
The application follows Angular testing conventions. All test files are located alongside their source files with a .spec.ts extension.
Current Test Files
The project includes tests for:
src/app/app.spec.ts - Application component tests
src/app/usuario/infrastructure/services/auth-service.spec.ts - Authentication service tests
src/app/usuario/infrastructure/ui/pages/login-page/login-page.spec.ts - Login page component tests
src/app/usuario/infrastructure/ui/pages/register-page/register-page.spec.ts - Register page component tests
Test Examples
Service Test Example
Here’s the authentication service test structure:
import { TestBed } from '@angular/core/testing' ;
import { AuthService } from './auth-service' ;
describe ( 'AuthService' , () => {
let service : AuthService ;
beforeEach (() => {
TestBed . configureTestingModule ({});
service = TestBed . inject ( AuthService );
});
it ( 'should be created' , () => {
expect ( service ). toBeTruthy ();
});
});
Key Points:
Use TestBed.configureTestingModule() to set up the testing module
Inject services using TestBed.inject()
Use beforeEach() to set up fresh instances for each test
Component Test Example
Here’s the login page component test structure:
import { ComponentFixture , TestBed } from '@angular/core/testing' ;
import { LoginPage } from './login-page' ;
describe ( 'LoginPage' , () => {
let component : LoginPage ;
let fixture : ComponentFixture < LoginPage >;
beforeEach ( async () => {
await TestBed . configureTestingModule ({
imports: [ LoginPage ]
})
. compileComponents ();
fixture = TestBed . createComponent ( LoginPage );
component = fixture . componentInstance ;
fixture . detectChanges ();
});
it ( 'should create' , () => {
expect ( component ). toBeTruthy ();
});
});
Key Points:
Use ComponentFixture to interact with the component
Call compileComponents() to compile templates and styles
Use fixture.detectChanges() to trigger change detection
Import standalone components in the imports array
Register Page Test Example
import { ComponentFixture , TestBed } from '@angular/core/testing' ;
import { RegisterPage } from './register-page' ;
describe ( 'RegisterPage' , () => {
let component : RegisterPage ;
let fixture : ComponentFixture < RegisterPage >;
beforeEach ( async () => {
await TestBed . configureTestingModule ({
imports: [ RegisterPage ]
})
. compileComponents ();
fixture = TestBed . createComponent ( RegisterPage );
component = fixture . componentInstance ;
fixture . detectChanges ();
});
it ( 'should create' , () => {
expect ( component ). toBeTruthy ();
});
});
Writing New Tests
Create a test file
Create a file with the .spec.ts extension next to your source file: my-component.ts
my-component.spec.ts
Import testing utilities
Import the necessary testing utilities from Angular and Jasmine: import { TestBed } from '@angular/core/testing' ;
import { ComponentFixture } from '@angular/core/testing' ;
Set up the test suite
Use describe() to create a test suite and beforeEach() to set up: describe ( 'MyComponent' , () => {
let component : MyComponent ;
let fixture : ComponentFixture < MyComponent >;
beforeEach ( async () => {
await TestBed . configureTestingModule ({
imports: [ MyComponent ]
}). compileComponents ();
fixture = TestBed . createComponent ( MyComponent );
component = fixture . componentInstance ;
});
});
Write test cases
Use it() to write individual test cases: it ( 'should display the correct title' , () => {
component . title = 'Test Title' ;
fixture . detectChanges ();
const compiled = fixture . nativeElement ;
expect ( compiled . querySelector ( 'h1' ). textContent ). toContain ( 'Test Title' );
});
Test Conventions
Follow these conventions when writing tests:
Naming
Test files: *.spec.ts
Test suites: Use descriptive names matching the component/service name
Test cases: Start with “should” and describe the expected behavior
Organization
describe ( 'ComponentName' , () => {
// Setup
let component : ComponentName ;
let fixture : ComponentFixture < ComponentName >;
beforeEach (() => {
// Arrange - Set up test environment
});
afterEach (() => {
// Clean up if needed
});
it ( 'should do something' , () => {
// Arrange - Set up test data
// Act - Perform action
// Assert - Verify result
});
});
Best Practices
Isolated Tests Each test should be independent and not rely on other tests
Clear Assertions Use clear, specific assertions that test one thing at a time
Mock Dependencies Mock external dependencies like HTTP services
Test Behavior Focus on testing behavior, not implementation details
Mocking and Spies
Mocking Services
const mockAuthService = {
login: jasmine . createSpy ( 'login' ). and . returnValue ( of ({ token: 'fake-token' }))
};
TestBed . configureTestingModule ({
providers: [
{ provide: AuthService , useValue: mockAuthService }
]
});
Using Spies
it ( 'should call the login method' , () => {
spyOn ( authService , 'login' ). and . returnValue ( of ({ token: 'test' }));
component . submit ();
expect ( authService . login ). toHaveBeenCalled ();
});
Coverage
To generate and view test coverage:
Run tests with coverage
ng test --code-coverage --watch=false
Review metrics
Check coverage for:
Statements : Lines of code executed
Branches : Conditional paths tested
Functions : Functions called
Lines : Individual lines executed
Aim for at least 80% code coverage, but focus on meaningful tests rather than just achieving high coverage numbers.
Troubleshooting
If Karma can’t find Chrome: Option 1: Install Chrome
Download and install Google Chrome
Option 2: Use ChromeHeadless ng test --browsers=ChromeHeadless
Option 3: Use a different browser ng test --browsers=Firefox
Tests fail after dependency updates
If tests fail after updating dependencies:
Clear the cache:
Reinstall dependencies:
rm -rf node_modules package-lock.json
npm install
Update test configurations if needed
Memory issues with large test suites
If you encounter memory issues: NODE_OPTIONS = --max_old_space_size = 8192 ng test
If async tests timeout, increase the timeout or use fakeAsync: import { fakeAsync , tick } from '@angular/core/testing' ;
it ( 'should handle async operation' , fakeAsync (() => {
component . loadData ();
tick ( 1000 );
expect ( component . data ). toBeDefined ();
}));
Continuous Integration
For CI/CD pipelines, use:
ng test --watch=false --code-coverage --browsers=ChromeHeadless
This will:
Run tests once without watching
Generate coverage reports
Use headless Chrome (no GUI)
Exit with appropriate status code
Next Steps