Skip to main content

Testing Basics

Level: Intermediate

ℹ️ What You'll Learn
  • Unit testing framework (Jasmine)
  • TestBed setup
  • Testing components
  • Testing services
  • Mocking HTTP
  • Running tests

Test Structure

// student.service.spec.ts
import { TestBed } from '@angular/core/testing';
import { StudentService } from './student.service';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';

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

beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [StudentService]
});
service = TestBed.inject(StudentService);
httpMock = TestBed.inject(HttpTestingController);
});

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

it('should load students', () => {
const mockStudents = [
{ id: 1, name: 'Ravi' },
{ id: 2, name: 'Priya' }
];

service.getStudents().subscribe(students => {
expect(students.length).toBe(2);
expect(students[0].name).toBe('Ravi');
});

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

Testing Components

// student-list.component.spec.ts
describe('StudentListComponent', () => {
let component: StudentListComponent;
let fixture: ComponentFixture<StudentListComponent>;
let studentService: jasmine.SpyObj<StudentService>;

beforeEach(async () => {
const spy = jasmine.createSpyObj('StudentService', ['getStudents']);

await TestBed.configureTestingModule({
declarations: [StudentListComponent],
providers: [
{ provide: StudentService, useValue: spy }
]
}).compileComponents();

fixture = TestBed.createComponent(StudentListComponent);
component = fixture.componentInstance;
studentService = TestBed.inject(StudentService) as jasmine.SpyObj<StudentService>;
});

it('should load students on init', () => {
const mockStudents = [{ id: 1, name: 'Ravi' }];
studentService.getStudents.and.returnValue(of(mockStudents));

fixture.detectChanges();

expect(studentService.getStudents).toHaveBeenCalled();
expect(component.students).toEqual(mockStudents);
});

it('should display student names', () => {
component.students = [{ id: 1, name: 'Ravi' }];
fixture.detectChanges();

const compiled = fixture.nativeElement;
expect(compiled.textContent).toContain('Ravi');
});
});

Run Tests

# Run tests
ng test

# Run with coverage
ng test --code-coverage

# Run once (CI)
ng test --watch=false

Opens browser, runs tests automatically.

Key Testing Patterns

// Test HTTP calls
it('should create student', () => {
const student = { name: 'Ravi', email: 'ravi@school.in' };

service.createStudent(student).subscribe(result => {
expect(result.id).toBe(1);
});

const req = httpMock.expectOne('/api/students');
req.flush({ id: 1, ...student });
});

// Test error handling
it('should handle errors', () => {
let error: any;

service.getStudents().subscribe(
() => {},
err => error = err
);

const req = httpMock.expectOne('/api/students');
req.error(new ErrorEvent('Network error'));

expect(error).toBeTruthy();
});

// Test component interaction
it('should delete student', () => {
spyOn(service, 'deleteStudent').and.returnValue(of(null));

component.deleteStudent(1);

expect(service.deleteStudent).toHaveBeenCalledWith(1);
});

SMS Test Example

describe('StudentListComponent', () => {
let component: StudentListComponent;
let fixture: ComponentFixture<StudentListComponent>;
let service: jasmine.SpyObj<StudentService>;

beforeEach(async () => {
const serviceStub = jasmine.createSpyObj('StudentService', [
'getStudents',
'deleteStudent'
]);

await TestBed.configureTestingModule({
declarations: [StudentListComponent],
providers: [
{ provide: StudentService, useValue: serviceStub }
]
}).compileComponents();

fixture = TestBed.createComponent(StudentListComponent);
component = fixture.componentInstance;
service = TestBed.inject(StudentService) as jasmine.SpyObj<StudentService>;
});

it('should load and display students', () => {
const mockStudents = [
{ id: 101, name: 'Ravi Kumar', className: '10' },
{ id: 102, name: 'Priya Sharma', className: '10' }
];

service.getStudents.and.returnValue(of(mockStudents));
fixture.detectChanges();

expect(component.students.length).toBe(2);
expect(component.students[0].name).toBe('Ravi Kumar');
});

it('should delete student', () => {
service.deleteStudent.and.returnValue(of(null));
component.students = [{ id: 101, name: 'Ravi' }];

component.deleteStudent(101);

expect(service.deleteStudent).toHaveBeenCalledWith(101);
});
});

Key Takeaways

  • Jasmine = testing framework
  • TestBed = configure testing module
  • HttpTestingModule = mock HTTP
  • Spies = mock functions
  • describe/it = test structure
  • Run with ng test
💡 Backend Developer Tip

Angular testing similar to NUnit/xUnit in .NET. Arrange-Act-Assert pattern applies.

⚠️ Testing Issues
  1. Not mocking dependencies — Real HTTP calls
  2. Testing implementation — Test behavior, not code
  3. Async not handled — fakeAsync/done() needed
  4. Too many assertions — One concern per test
🤖Use AI to Learn Faster

Use ChatGPT, Claude, or Copilot to go deeper on Angular Testing. Try these prompts:

  • "Why mock services instead of using real ones?"
  • "What's HttpTestingModule for?"
  • "How do you test component user interactions?"
  • "Quiz me on testing"

💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.

nexcoding.in