Skip to main content
Angular Basics
CHAPTER 25 Beginner

Angular Testing

Updated: May 18, 2026
5 min read

# CHAPTER 25

Angular Testing

1. Chapter Introduction

Testing is what separates professional applications from prototypes. Angular ships with a complete testing setup out of the box: Jasmine (the testing framework for writing tests) and Karma (the test runner that executes them in the browser). The Angular CLI generates .spec.ts test files automatically for every component and service.

2. Learning Objectives

  • Understand the Angular testing setup (Jasmine + Karma).
  • Write unit tests for pure TypeScript functions.
  • Test Angular Services with mocked dependencies.
  • Test Components using TestBed and ComponentFixture.
  • Test Observables.

3. Running Tests

bash
1
ng test   # Runs all tests, watches for changes

4. Testing Pure Functions

Start simple — test TypeScript functions with no Angular dependencies:
math.utils.ts
123
export function calculateTotal(price: number, quantity: number): number {
  return price * quantity;
}
math.utils.spec.ts
123456789101112131415
import { calculateTotal } from './math.utils';

describe('calculateTotal', () => {
  it('should multiply price by quantity', () => {
    expect(calculateTotal(10, 5)).toBe(50);
  });

  it('should return 0 for zero quantity', () => {
    expect(calculateTotal(100, 0)).toBe(0);
  });

  it('should handle decimal prices', () => {
    expect(calculateTotal(9.99, 3)).toBeCloseTo(29.97);
  });
});

5. Testing Angular Services

task.service.spec.ts
123456789101112131415161718192021222324252627282930313233
import { TestBed } from '@angular/core/testing';
import { TaskService } from './task.service';

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

  beforeEach(() => {
    TestBed.configureTestingModule({});
    service = TestBed.inject(TaskService);
  });

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

  it('should start with initial tasks', () => {
    const tasks = service.getTasks();
    expect(tasks.length).toBeGreaterThan(0);
  });

  it('should add a new task', () => {
    const initialCount = service.getTasks().length;
    service.addTask('New test task');
    expect(service.getTasks().length).toBe(initialCount + 1);
  });

  it('should delete a task by index', () => {
    service.addTask('Task to delete');
    const countBefore = service.getTasks().length;
    service.deleteTask(countBefore - 1);
    expect(service.getTasks().length).toBe(countBefore - 1);
  });
});

6. Testing Components with TestBed

counter.component.spec.ts
123456789101112131415161718192021222324252627282930313233343536
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CounterComponent } from './counter.component';

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

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

    fixture = TestBed.createComponent(CounterComponent);
    component = fixture.componentInstance;
    fixture.detectChanges(); // Triggers ngOnInit
  });

  it(&#039;should create&#039;, () => {
    expect(component).toBeTruthy();
  });

  it(&#039;should display initial count of 0&#039;, () => {
    const h2: HTMLElement = fixture.nativeElement.querySelector(&#039;h2&#039;);
    expect(h2.textContent).toContain(&#039;0&#039;);
  });

  it(&#039;should increment count when button is clicked&#039;, () => {
    const button: HTMLButtonElement = fixture.nativeElement.querySelector(&#039;button&#039;);
    button.click();
    fixture.detectChanges(); // Update the view

    const h2: HTMLElement = fixture.nativeElement.querySelector(&#039;h2&#039;);
    expect(h2.textContent).toContain(&#039;1&#039;);
    expect(component.count).toBe(1);
  });
});

7. Mocking Services in Component Tests

user-list.component.spec.ts
12345678910111213141516171819202122232425262728
import { of } from &#039;rxjs&#039;;
import { UserService } from &#039;../user.service&#039;;

describe(&#039;UserListComponent&#039;, () => {
  let mockUserService: jasmine.SpyObj<UserService>;

  beforeEach(async () => {
    mockUserService = jasmine.createSpyObj(&#039;UserService&#039;, [&#039;getUsers&#039;]);
    // Return fake data instead of real HTTP call
    mockUserService.getUsers.and.returnValue(of([
      { id: 1, name: &#039;Alice&#039; },
      { id: 2, name: &#039;Bob&#039; }
    ]));

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

  it(&#039;should display 2 users&#039;, () => {
    fixture.detectChanges();
    const items = fixture.nativeElement.querySelectorAll(&#039;li&#039;);
    expect(items.length).toBe(2);
  });
});

8. Jasmine Matchers Reference

typescript
123456789101112
expect(value).toBe(exact);           // Strict equality ===
expect(value).toEqual(deepEqual);    // Deep equality (objects, arrays)
expect(value).toBeTruthy();          // Not null, 0, '', false
expect(value).toBeFalsy();           // null, 0, '', false
expect(value).toBeNull();
expect(value).toBeUndefined();
expect(value).toContain(&#039;substring&#039;);
expect(arr).toContain(item);
expect(value).toBeGreaterThan(num);
expect(fn).toThrow();                // Function throws an error
expect(spy).toHaveBeenCalled();      // Spy was called
expect(spy).toHaveBeenCalledWith(arg); // Spy called with specific args

9. Common Mistakes

  • Not calling fixture.detectChanges(): After any state change (clicking a button, updating data), you must call this to trigger Angular's change detection and update the DOM.
  • Testing implementation details: Test behavior, not internals. Test what the user sees, not how the component achieves it.

10. MCQs with Answers

Question 1

What testing framework does Angular use by default?

Question 2

What Angular CLI command runs the test suite?

Question 3

What class configures an Angular testing module?

Question 4

What does fixture.detectChanges() do?

Question 5

How do you create a mock service in Jasmine?

Question 6

What does toBeTruthy() check?

Question 7

What is the purpose of beforeEach() in a Jasmine test suite?

Question 8

What is a Jasmine Spy?

Question 9

What method is used to make an Observable spy return test data?

Question 10

What file extension do Angular test files use?

11. Interview Questions

  • Q: What is the difference between a unit test and an integration test? Give an example of each in Angular.
  • Q: Why do we mock services in component tests rather than using the real service?

12. Summary

Testing in Angular is a first-class feature with complete tooling provided by the CLI. By writing unit tests for services and component tests using TestBed, developers create a safety net that allows confident refactoring, fearless feature additions, and faster debugging.

13. Next Chapter Recommendation

In Chapter 26: Angular with Firebase, we integrate our Angular app with Google's Firebase platform for serverless authentication, real-time database, and cloud hosting.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·