| |

Angular 101 🅰️ Testing — Component and Service Tests

⏱️ Estimated read: 17 min | |

The previous chapter covered the testing infrastructure: Jasmine for the test syntax, Karma for the test runner, TestBed for the testing module, and ComponentFixture for the component wrapper. This chapter goes deeper into the two most common kinds of tests in an Angular application: component tests and service tests. They are different because components have templates and services do not. The tools are different because the problems are different.

A component test verifies what the user sees and what the user does. It renders the component, queries the DOM, simulates interactions, and checks that the template updates correctly. A service test verifies the logic in isolation. It instantiates the service, calls its methods, and checks the results. Some services have no dependencies and can be tested with new. Some have HTTP dependencies and use HttpTestingController. Some have other services and use spies or mock providers.

Key point: A component test is a test of the rendered output and the user interaction. A service test is a test of the logic. The two should not be mixed. A component test should not test the service’s logic — that is the service test’s job. A service test should not test the component’s template — that is the component test’s job. Keeping the tests focused makes them faster to write, faster to run, and easier to debug.


Why component and service tests matter

A component test verifies that the component renders correctly and responds to user input. A service test verifies that the service computes correctly and handles its dependencies. Both are necessary. A component that renders correctly but calls a broken service is still broken. A service that computes correctly but is never called by the component is useless.

The DOM problem. A component’s behavior is expressed in its template. The binding updates when the state changes. The event handlers fire when the user interacts. The component test verifies that the template reflects the state and that the interactions produce the expected state changes.

The isolation problem. A service’s behavior is expressed in its methods. The methods take inputs and return outputs. The service test verifies that the outputs are correct for the given inputs. The service test does not need to render anything.

The dependency problem. A service with dependencies needs the dependencies to be provided. The service test uses TestBed to provide the dependencies or spies to replace them. The choice depends on whether the test needs the real dependency or a controlled version.

The async problem. A component with asynchronous behavior (HTTP, timers, observables) needs the test to wait for the async operation. The async and await keywords, the fakeAsync and tick functions, and the whenStable method handle the different async scenarios.

The trade-off. Component tests are slower than service tests because they render the template and run change detection. Service tests are faster because they only execute the logic. The team should write more service tests than component tests, and the component tests should focus on the rendering and the interaction, not the logic.


a. Component Tests

A component test uses the TestBed to configure the testing module, createComponent to create the component, and fixture.detectChanges to render the template. The test then queries the DOM and simulates interactions.

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({
      imports: [CounterComponent],
    }).compileComponents();

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

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

  it('should start with count 0', () => {
    expect(component.count()).toBe(0);
  });

  it('should increment the count when the button is clicked', () => {
    const button = fixture.nativeElement.querySelector('button');
    button.click();
    fixture.detectChanges();
    expect(component.count()).toBe(1);
  });

  it('should display the count in the template', () => {
    component.increment();
    fixture.detectChanges();
    const p = fixture.nativeElement.querySelector('p');
    expect(p.textContent).toContain('Count: 1');
  });
});

The beforeEach block creates the component and calls detectChanges to render the initial state. Each it block tests a specific behavior. The fixture.nativeElement is the component’s host element. The querySelector finds an element in the rendered DOM. The click() method simulates a click. The detectChanges() method updates the DOM after the click.

The component test can also test the component’s inputs and outputs. The fixture.componentRef.setInput method sets an input. The component.outputName.subscribe subscribes to an output.

it('should display the user name', () => {
  fixture.componentRef.setInput('user', { id: 1, name: 'Alice' });
  fixture.detectChanges();
  const h3 = fixture.nativeElement.querySelector('h3');
  expect(h3.textContent).toContain('Alice');
});

it('should emit the selected user', () => {
  const selectedUsers: User[] = [];
  component.select.subscribe((user) => selectedUsers.push(user));

  fixture.componentRef.setInput('user', { id: 1, name: 'Alice' });
  fixture.detectChanges();
  fixture.nativeElement.querySelector('button').click();

  expect(selectedUsers.length).toBe(1);
  expect(selectedUsers[0].name).toBe('Alice');
});

The setInput method sets the input and triggers change detection. The subscribe method listens to the output. The test verifies that the output emits the correct value.

A component with a service dependency uses TestBed to provide the service. The service can be the real service or a mock.

import { TestBed } from '@angular/core/testing';
import { UserListComponent } from './user-list.component';
import { UserService } from './user.service';

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

  beforeEach(async () => {
    const spy = jasmine.createSpyObj('UserService', ['getUsers']);
    spy.getUsers.and.returnValue(of([{ id: 1, name: 'Alice' }]));

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

    fixture = TestBed.createComponent(UserListComponent);
    component = fixture.componentInstance;
    userService = TestBed.inject(UserService) as jasmine.SpyObj<UserService>;
    fixture.detectChanges();
  });

  it('should display the users from the service', () => {
    const items = fixture.nativeElement.querySelectorAll('li');
    expect(items.length).toBe(1);
    expect(items[0].textContent).toContain('Alice');
  });
});

The jasmine.createSpyObj creates a mock service with the getUsers method. The providers array tells TestBed to use the mock instead of the real service. The spy.getUsers.and.returnValue sets the return value. The test verifies that the component renders the users from the service.


b. Service Tests

A service test is simpler than a component test. The service has no template. The test instantiates the service, calls its methods, and checks the results.

import { TestBed } from '@angular/core/testing';
import { CalculatorService } from './calculator.service';

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

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

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

  it('should add two numbers', () => {
    expect(service.add(2, 3)).toBe(5);
  });

  it('should subtract two numbers', () => {
    expect(service.subtract(5, 3)).toBe(2);
  });

  it('should multiply two numbers', () => {
    expect(service.multiply(4, 5)).toBe(20);
  });

  it('should divide two numbers', () => {
    expect(service.divide(10, 2)).toBe(5);
  });

  it('should throw when dividing by zero', () => {
    expect(() => service.divide(10, 0)).toThrowError('Division by zero');
  });
});

The TestBed.configureTestingModule with an empty configuration is enough for a service with no dependencies. The TestBed.inject retrieves the service instance. The test calls the service’s methods and checks the results.

A service with a dependency uses TestBed to provide the dependency. The dependency can be the real service or a mock.

import { TestBed } from '@angular/core/testing';
import { UserService } from './user.service';
import { HttpClient } from '@angular/common/http';
import { of } from 'rxjs';

describe('UserService', () => {
  let service: UserService;
  let httpSpy: jasmine.SpyObj<HttpClient>;

  beforeEach(() => {
    const spy = jasmine.createSpyObj('HttpClient', ['get']);
    spy.get.and.returnValue(of([{ id: 1, name: 'Alice' }]));

    TestBed.configureTestingModule({
      providers: [
        UserService,
        { provide: HttpClient, useValue: spy },
      ],
    });
    service = TestBed.inject(UserService);
    httpSpy = TestBed.inject(HttpClient) as jasmine.SpyObj<HttpClient>;
  });

  it('should fetch users from the API', () => {
    service.getUsers().subscribe((users) => {
      expect(users.length).toBe(1);
      expect(users[0].name).toBe('Alice');
    });
    expect(httpSpy.get).toHaveBeenCalledWith('/api/users');
  });
});

The jasmine.createSpyObj creates a mock HttpClient. The providers array provides the mock instead of the real HttpClient. The spy.get.and.returnValue sets the return value. The test verifies that the service calls the HTTP client with the correct URL and returns the correct data.

A service with an HTTP dependency can also use HttpTestingController for a more realistic test.

import { TestBed } from '@angular/core/testing';
import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting, HttpTestingController } from '@angular/common/http/testing';
import { UserService } from './user.service';

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

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        provideHttpClient(),
        provideHttpClientTesting(),
      ],
    });
    service = TestBed.inject(UserService);
    httpMock = TestBed.inject(HttpTestingController);
  });

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

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

    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 a 404 error', () => {
    service.getUsers().subscribe({
      next: () => fail('Expected an error'),
      error: (error) => {
        expect(error.status).toBe(404);
      },
    });

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

The provideHttpClient and provideHttpClientTesting providers set up the HTTP client with the testing backend. The httpMock.expectOne method asserts that a request was made to the specified URL. The req.flush method provides the mock response. The httpMock.verify method asserts that no unexpected requests were made.


c. Async Tests

A component or service that uses asynchronous operations needs an async test. Angular provides several tools for different scenarios.

The fakeAsync and tick functions wrap the test in a fake timer context. The tick function advances the virtual clock.

import { fakeAsync, tick } from '@angular/core/testing';

it('should resolve after a delay', fakeAsync(() => {
  let result: string | undefined;
  service.getDelayedValue().then((value) => (result = value));
  tick(1000);
  expect(result).toBe('done');
}));

The async and await keywords work with promises. The fixture.whenStable method returns a promise that resolves when the component is stable.

it('should fetch users asynchronously', async () => {
  await fixture.whenStable();
  const items = fixture.nativeElement.querySelectorAll('li');
  expect(items.length).toBe(1);
});

The whenStable method waits for the component to finish all pending async operations. The test then queries the DOM.

The waitForAsync function wraps the test in a zone that tracks the async operations.

import { waitForAsync } from '@angular/core/testing';

it('should fetch users', waitForAsync(() => {
  service.getUsers().subscribe((users) => {
    expect(users.length).toBe(1);
  });
}));

The waitForAsync function is the older API. The async and await keywords are the modern approach.


Complete Example Session

This session tests a counter component, a user list component with a service, and a user service with HTTP.

// ============================================
// PART 1: THE COUNTER COMPONENT
// ============================================

import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-counter',
  template: `
    <p>Count: {{ count() }}</p>
    <button (click)="increment()">+</button>
    <button (click)="decrement()">−</button>
  `,
})
export class CounterComponent {
  count = signal(0);
  increment(): void { this.count.update((v) => v + 1); }
  decrement(): void { this.count.update((v) => v - 1); }
}

// ============================================
// PART 2: THE COUNTER COMPONENT TEST
// ============================================

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({
      imports: [CounterComponent],
    }).compileComponents();

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

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

  it('should start with count 0', () => {
    expect(component.count()).toBe(0);
  });

  it('should increment when the + button is clicked', () => {
    const buttons = fixture.nativeElement.querySelectorAll('button');
    buttons[0].click();
    fixture.detectChanges();
    expect(component.count()).toBe(1);
  });

  it('should decrement when the − button is clicked', () => {
    const buttons = fixture.nativeElement.querySelectorAll('button');
    buttons[1].click();
    fixture.detectChanges();
    expect(component.count()).toBe(-1);
  });

  it('should display the count in the template', () => {
    component.increment();
    fixture.detectChanges();
    const p = fixture.nativeElement.querySelector('p');
    expect(p.textContent).toContain('Count: 1');
  });
});

// ============================================
// PART 3: THE USER SERVICE
// ============================================

import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export interface User {
  id: number;
  name: string;
}

@Injectable({ providedIn: 'root' })
export class UserService {
  private http = inject(HttpClient);
  getUsers(): Observable<User[]> {
    return this.http.get<User[]>('/api/users');
  }
}

// ============================================
// PART 4: THE USER SERVICE TEST
// ============================================

import { TestBed } from '@angular/core/testing';
import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting, HttpTestingController } from '@angular/common/http/testing';
import { UserService, User } from './user.service';

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

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        provideHttpClient(),
        provideHttpClientTesting(),
      ],
    });
    service = TestBed.inject(UserService);
    httpMock = TestBed.inject(HttpTestingController);
  });

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

  it('should fetch users', () => {
    const mockUsers: User[] = [{ id: 1, name: 'Alice' }];
    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 an error', () => {
    service.getUsers().subscribe({
      next: () => fail('Expected an error'),
      error: (error) => expect(error.status).toBe(500),
    });
    const req = httpMock.expectOne('/api/users');
    req.flush('Server error', { status: 500, statusText: 'Internal Server Error' });
  });
});

// ============================================
// PART 5: THE USER LIST COMPONENT
// ============================================

import { Component, inject, signal } from '@angular/core';
import { UserService, User } from './user.service';

@Component({
  selector: 'app-user-list',
  template: `
    <ul>
      @for (user of users(); track user.id) {
        <li>{{ user.name }}</li>
      }
    </ul>
  `,
})
export class UserListComponent {
  private userService = inject(UserService);
  users = signal<User[]>([]);

  ngOnInit(): void {
    this.userService.getUsers().subscribe((users) => this.users.set(users));
  }
}

// ============================================
// PART 6: THE USER LIST COMPONENT TEST
// ============================================

import { TestBed } from '@angular/core/testing';
import { of } from 'rxjs';
import { UserListComponent } from './user-list.component';
import { UserService } from './user.service';

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

  beforeEach(async () => {
    const spy = jasmine.createSpyObj('UserService', ['getUsers']);
    spy.getUsers.and.returnValue(of([{ id: 1, name: 'Alice' }]));

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

    fixture = TestBed.createComponent(UserListComponent);
    userService = TestBed.inject(UserService) as jasmine.SpyObj<UserService>;
    fixture.detectChanges();
  });

  it('should display the users', () => {
    const items = fixture.nativeElement.querySelectorAll('li');
    expect(items.length).toBe(1);
    expect(items[0].textContent).toContain('Alice');
  });
});

// ============================================
// PART 7: THE ASYNC TEST
// ============================================

import { fakeAsync, tick } from '@angular/core/testing';

it('should resolve after a delay', fakeAsync(() => {
  let result: string | undefined;
  service.getDelayedValue().then((value) => (result = value));
  tick(1000);
  expect(result).toBe('done');
}));

// ============================================
// PART 8: THE WAIT FOR STABLE
// ============================================

it('should render users after stable', async () => {
  await fixture.whenStable();
  fixture.detectChanges();
  const items = fixture.nativeElement.querySelectorAll('li');
  expect(items.length).toBe(1);
});

// ============================================
// PART 9: THE SPY ON METHOD
// ============================================

it('should call the service', () => {
  const userService = TestBed.inject(UserService);
  spyOn(userService, 'getUsers').and.returnValue(of([]));
  component.ngOnInit();
  expect(userService.getUsers).toHaveBeenCalled();
});

// ============================================
// PART 10: THE TESTING CHECKLIST
// ============================================

// 1. Test every component
// 2. Test every service
// 3. Test the happy path
// 4. Test the edge cases
// 5. Test the error cases
// 6. Use TestBed for components with dependencies
// 7. Use HttpTestingController for HTTP
// 8. Use spies for external dependencies
// 9. Use fakeAsync for timers
// 10. Run the tests in CI

The ten parts cover the counter component, the counter component test, the user service, the user service test, the user list component, the user list component test, the async test, the wait for stable, the spy on method, and the testing checklist.


Quick Reference

The Component Test Tools

ToolPurpose
TestBed.configureTestingModuleConfigure the test module
TestBed.createComponentCreate the component
fixture.detectChangesRender the template
fixture.nativeElementThe DOM element
fixture.componentInstanceThe component
fixture.componentRef.setInputSet an input
fixture.whenStableWait for async stability
querySelectorFind an element
querySelectorAllFind all elements

The Service Test Tools

ToolPurpose
TestBed.injectRetrieve the service
jasmine.createSpyObjCreate a mock service
spyOnReplace a method
spy.and.returnValueSet the return value
HttpTestingControllerIntercept HTTP requests
httpMock.expectOneAssert a request
req.flushProvide a response
httpMock.verifyAssert no pending requests

The Async Test Tools

ToolPurpose
fakeAsyncFake timer context
tickAdvance the virtual clock
async / awaitWait for promises
whenStableWait for stability
waitForAsyncOlder async helper

The Common Matchers

MatcherPurpose
toBeStrict equality
toEqualDeep equality
toBeTruthyTruthy value
toBeFalsyFalsy value
toContainContains a value
toThrowErrorThrows an error
toHaveBeenCalledSpy was called
toHaveBeenCalledWithSpy was called with args

Best Practices

✅ Do This:

// Use TestBed for component tests
await TestBed.configureTestingModule({ imports: [CounterComponent] }).compileComponents(); // ✅
// Call detectChanges after state changes
component.increment();
fixture.detectChanges();                                         // ✅
// Use HttpTestingController for HTTP
const req = httpMock.expectOne('/api/users');
req.flush(mockUsers);                                            // ✅
// Use spies for external dependencies
spyOn(userService, 'getUsers').and.returnValue(of([]));          // ✅
// Use fakeAsync for timers
it('should resolve', fakeAsync(() => { tick(1000); }));          // ✅

❌ Don’t Do This:

// Don't test the service logic in the component test
expect(component.userService.calculate()).toBe(5);               // ❌
// Don't forget to flush HTTP requests
httpMock.expectOne('/api/users');  // no flush → test hangs      // ❌
// Don't use real HTTP in unit tests
http.get('/api/users');  // use HttpTestingController            // ❌
// Don't rely on test order
// Each test should be independent.                              // ❌

Common Pitfalls

PitfallWhy It HappensFix
Test hangsHTTP request not flushedUse req.flush()
Change detection not runningdetectChanges not calledCall fixture.detectChanges()
Service not foundProvider not configuredAdd to providers
Async test failsPromise not awaitedUse fakeAsync and tick
Test depends on orderShared stateReset in beforeEach
Component not renderingTemplate not compiledCall compileComponents

Real-World Examples

1. Component Test

fixture = TestBed.createComponent(CounterComponent);
component = fixture.componentInstance;

2. DOM Query

const button = fixture.nativeElement.querySelector('button');

3. Click Simulation

button.click();
fixture.detectChanges();

4. Set Input

fixture.componentRef.setInput('user', { id: 1, name: 'Alice' });

5. Subscribe to Output

component.select.subscribe((user) => selectedUsers.push(user));

6. Service Test

service = TestBed.inject(CalculatorService);

7. HTTP Test

const req = httpMock.expectOne('/api/users');
req.flush(mockUsers);

8. Spy

spyOn(userService, 'getUsers').and.returnValue(of([]));

9. Async Test

it('should resolve', fakeAsync(() => {
  let result;
  service.getValue().then(v => result = v);
  tick(1000);
  expect(result).toBe('done');
}));

10. Error Test

expect(() => service.divide(10, 0)).toThrowError('Division by zero');

Visual

The Component Test Flow

┌──────────────────────────────────────────────┐
│  COMPONENT TEST FLOW                         │
│                                              │
│  1. TestBed.configureTestingModule           │
│     └─ Declare the component and providers   │
│                                              │
│  2. compileComponents                        │
│     └─ Compile the template                  │
│                                              │
│  3. createComponent                          │
│     └─ Create the component and fixture      │
│                                              │
│  4. detectChanges                            │
│     └─ Render the initial state              │
│                                              │
│  5. querySelector / click                    │
│     └─ Interact with the DOM                 │
│                                              │
│  6. detectChanges                            │
│     └─ Update the DOM                        │
│                                              │
│  7. expect                                   │
│     └─ Assert the result                     │
│                                              │
└──────────────────────────────────────────────┘

The Service Test Flow

┌──────────────────────────────────────────────┐
│  SERVICE TEST FLOW                           │
│                                              │
│  1. TestBed.configureTestingModule           │
│     └─ Provide the service and dependencies  │
│                                              │
│  2. TestBed.inject                           │
│     └─ Retrieve the service instance         │
│                                              │
│  3. Call a method                            │
│     └─ service.getUsers()                    │
│                                              │
│  4. Assert                                   │
│     └─ expect(result).toEqual(expected)      │
│                                              │
│  No DOM. No template.                        │
│  Just the logic.                             │
│                                              │
└──────────────────────────────────────────────┘

The HTTP Test Flow

┌──────────────────────────────────────────────┐
│  HTTP TEST FLOW                              │
│                                              │
│  service.getUsers().subscribe(...)           │
│       │                                      │
│       ▼                                      │
│  httpMock.expectOne('/api/users')            │
│       │                                      │
│       ▼                                      │
│  req.flush(mockUsers)                        │
│       │                                      │
│       ▼                                      │
│  The subscriber receives the data            │
│       │                                      │
│       ▼                                      │
│  httpMock.verify()                           │
│                                              │
│  The HttpTestingController intercepts the    │
│  request and provides the mock response.     │
│                                              │
└──────────────────────────────────────────────┘

The Async Test Tools

┌──────────────────────────────────────────────┐
│  ASYNC TEST TOOLS                            │
│                                              │
│  fakeAsync + tick:                           │
│    └─ Fake timer context                     │
│    └─ tick advances the clock                │
│                                              │
│  async / await:                              │
│    └─ Wait for real promises                 │
│                                              │
│  whenStable:                                 │
│    └─ Wait for the component to stabilize    │
│                                              │
│  waitForAsync:                               │
│    └─ Older API, wraps in a zone             │
│                                              │
└──────────────────────────────────────────────┘

Summary

ItemValue
Component testTestBed + ComponentFixture
Service testTestBed.inject
Renderfixture.detectChanges()
DOM queryfixture.nativeElement.querySelector
Set inputfixture.componentRef.setInput
Subscribe to outputcomponent.output.subscribe
HTTP testHttpTestingController
Spyjasmine.createSpyObj, spyOn
Async testfakeAsync, tick, whenStable
Test file*.spec.ts

Key takeaways:

  • A component test verifies what the user sees and what the user does. It renders the component, queries the DOM, simulates interactions, and checks that the template updates correctly. The TestBed configures the testing module. The ComponentFixture wraps the component .
  • A service test verifies the logic in isolation. It instantiates the service, calls its methods, and checks the results. A service with no dependencies can be tested with new. A service with dependencies uses TestBed to provide them .
  • The setInput method sets an input on the component. The subscribe method listens to an output. The test verifies that the output emits the correct value .
  • The HttpTestingController intercepts HTTP requests. The expectOne method asserts that a request was made. The flush method provides the mock response. The verify method asserts that no unexpected requests were made .
  • The jasmine.createSpyObj creates a mock service. The spyOn method replaces a method on an existing object. The and.returnValue method sets the return value. The toHaveBeenCalled matcher asserts that the spy was called .
  • The fakeAsync and tick functions handle timers. The fakeAsync function wraps the test in a fake timer context. The tick function advances the virtual clock. The whenStable method waits for the component to finish all pending async operations.
  • Keep the tests focused. A component test should test the rendering and the interaction. A service test should test the logic. Do not mix the two. The separation makes the tests faster to write, faster to run, and easier to debug.

Remember: Component tests and service tests are different. The component test verifies the rendering and the interaction. The service test verifies the logic. The tools are different because the problems are different. Use TestBed for both. Use ComponentFixture for the component. Use TestBed.inject for the service. Use HttpTestingController for HTTP. Use spies for external dependencies. Use fakeAsync for timers. Keep the tests focused, independent, and fast.

Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!