Skip to main content

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:
npm test
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:
ng test --code-coverage
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:
auth-service.spec.ts
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:
login-page.spec.ts
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

register-page.spec.ts
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

1

Create a test file

Create a file with the .spec.ts extension next to your source file:
my-component.ts
my-component.spec.ts
2

Import testing utilities

Import the necessary testing utilities from Angular and Jasmine:
import { TestBed } from '@angular/core/testing';
import { ComponentFixture } from '@angular/core/testing';
3

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;
  });
});
4

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:
1

Run tests with coverage

ng test --code-coverage --watch=false
2

Open coverage report

open coverage/index.html
3

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
If tests fail after updating dependencies:
  1. Clear the cache:
    rm -rf .angular/cache
    
  2. Reinstall dependencies:
    rm -rf node_modules package-lock.json
    npm install
    
  3. Update test configurations if needed
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

Build docs developers (and LLMs) love