Skip to content
All essays
WebMarch 27, 202514 min

Angular Testing Guide

Master Angular testing with TestBed, Jasmine, and Karma. Learn unit, integration, and E2E testing.

Ü
Ümit Uz
Mobile & Full Stack Developer

Testing is crucial for building reliable Angular applications. Learn unit, integration, and end-to-end testing.

Setup

`bash ng test # Unit tests ng e2e # E2E tests `

TestBed Setup

typescript
import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';

describe('UserService', () => {
  let service: UserService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [UserService]
    });

    service = TestBed.inject(UserService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });
});

Component Testing

typescript
import { ComponentFixture, TestBed } from '@angular/core/testing';

describe('UserListComponent', () => {
  let component: UserListComponent;
  let fixture: ComponentFixture<UserListComponent>;
  let mockUserService: jasmine.SpyObj<UserService>;

  beforeEach(async () => {
    mockUserService = jasmine.createSpyObj('UserService', ['getUsers']);
    mockUserService.getUsers.and.returnValue(of([
      { id: 1, name: 'John' },
      { id: 2, name: 'Jane' }
    ]));

    await TestBed.configureTestingModule({
      declarations: [UserListComponent],
      providers: [
        { provide: UserService, useValue: mockUserService }
      ]
    }).compileComponents();

    fixture = TestBed.createComponent(UserListComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should display users', () => {
    const elements = fixture.nativeElement.querySelectorAll('.user');
    expect(elements.length).toBe(2);
    expect(elements[0].textContent).toContain('John');
  });

  it('should call getUsers on init', () => {
    expect(mockUserService.getUsers).toHaveBeenCalled();
  });
});

Testing with Inputs and Outputs

typescript
describe('UserCardComponent', () => {
  let component: UserCardComponent;
  let fixture: ComponentFixture<UserCardComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [UserCardComponent]
    }).compileComponents();

    fixture = TestBed.createComponent(UserCardComponent);
    component = fixture.componentInstance;
  });

  it('should accept user input', () => {
    component.user = { id: 1, name: 'John' };
    fixture.detectChanges();

    expect(fixture.nativeElement.textContent).toContain('John');
  });

  it('should emit delete event', () => {
    spyOn(component.delete, 'emit');
    component.onDelete(1);
    expect(component.delete.emit).toHaveBeenCalledWith(1);
  });
});

Service Testing with HttpClient

typescript
import {
  HttpClientTestingModule,
  HttpTestingController
} from '@angular/common/http/testing';

describe('UserService with Http', () => {
  let service: UserService;
  let httpMock: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [UserService]
    });

    service = TestBed.inject(UserService);
    httpMock = TestBed.inject(HttpTestingController);
  });

  afterEach(() => {
    httpMock.verify();
  });

  it('should fetch users', () => {
    const mockUsers = [
      { id: 1, name: 'John' },
      { id: 2, name: 'Jane' }
    ];

    service.getUsers().subscribe(users => {
      expect(users).toEqual(mockUsers);
    });

    const req = httpMock.expectOne('/api/users');
    expect(req.request.method).toBe('GET');
    req.flush(mockUsers);
  });

  it('should handle errors', () => {
    service.getUsers().subscribe({
      next: () => fail('Should have failed'),
      error: error => expect(error.message).toContain('404')
    });

    const req = httpMock.expectOne('/api/users');
    req.flush('Error', { status: 404, statusText: 'Not Found' });
  });
});

Testing Directives

typescript
describe('HighlightDirective', () => {
  it('should highlight element', () => {
    const fixture = TestBed.createComponent(TestComponent);
    const directiveEl = fixture.nativeElement.querySelector('.highlight');

    directiveEl.dispatchEvent(new Event('mouseenter'));
    fixture.detectChanges();

    expect(directiveEl.style.backgroundColor).toBe('yellow');
  });
});

Testing Pipes

typescript
describe('TruncatePipe', () => {
  let pipe: TruncatePipe;

  beforeEach(() => {
    pipe = new TruncatePipe();
  });

  it('should truncate long text', () => {
    const result = pipe.transform('Hello World', 5);
    expect(result).toBe('Hello...');
  });

  it('should return short text unchanged', () => {
    const result = pipe.transform('Hi', 5);
    expect(result).toBe('Hi');
  });
});

Testing Routing

typescript
import { RouterTestingModule } from '@angular/router/testing';

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

  it('should navigate to home', () => {
    const fixture = TestBed.createComponent(NavigationComponent);
    const router = TestBed.inject(Router);
    spyOn(router, 'navigateByUrl');

    fixture.nativeElement.querySelector('[routerLink="/"]').click();
    fixture.detectChanges();

    expect(router.navigateByUrl).toHaveBeenCalledWith('/');
  });
});

Mocking Services

typescript
class MockUserService {
  getUsers() {
    return of([
      { id: 1, name: 'John' }
    ]);
  }
}

describe('With Mock', () => {
  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [UserListComponent],
      providers: [
        { provide: UserService, useClass: MockUserService }
      ]
    }).compileComponents();
  });
});

Integration Testing

typescript
describe('UserForm Integration', () => {
  let component: UserFormComponent;
  let fixture: ComponentFixture<UserFormComponent>;
  let userService: jasmine.SpyObj<UserService>;

  beforeEach(async () => {
    userService = jasmine.createSpyObj('UserService', ['createUser']);

    await TestBed.configureTestingModule({
      declarations: [UserFormComponent],
      providers: [
        { provide: UserService, useValue: userService }
      ]
    }).compileComponents();

    fixture = TestBed.createComponent(UserFormComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should submit form', () => {
    userService.createUser.and.returnValue(of({ id: 1, name: 'John' }));

    component.form.setValue({ name: 'John', email: 'john@example.com' });
    component.onSubmit();

    expect(userService.createUser).toHaveBeenCalledWith({
      name: 'John',
      email: 'john@example.com'
    });
  });
});

Best Practices

  1. 1Test user behavior, not implementation
  2. 2Use meaningful test names
  3. 3Keep tests independent
  4. 4Mock external dependencies
  5. 5Test edge cases and error scenarios

Angular testing ensures your application is reliable and maintainable.

Enjoyed this essay? Share it.

Related essays

Next essay
Angular Dependency Injection