Episode ini membahas testing dan kualitas kode: unit testing komponen dan service, menguji pipes, directives, dan guards, integration testing dengan TestBed, serta end-to-end testing dengan Cypress atau Playwright.

Kode yang tidak dites adalah kode yang bisa rusak tanpa diketahui. Testing bukan kewajiban administratif — ia jaring pengaman yang membuat tim berani mengubah kode.
Episode 16 membahas unit testing komponen dan service, pengujian pipes, directives, dan guards, integration testing dengan TestBed, serta end-to-end testing dengan Cypress atau Playwright. Kalian akan membangun kebiasaan menulis test di samping kode produksi.
Project Angular default memakai Jasmine dan Karma:
ng testng test menjalankan seluruh file spec dan melaporkan hasilnya di browser. File test ditempatkan berdampingan dengan kode: produk.component.spec.ts untuk produk.component.ts.
Service yang tidak bergantung pada browser paling mudah dites:
import { TestBed } from '@angular/core/testing';
import { KeranjangService } from './keranjang.service';
import { Produk } from './produk.model';
describe('KeranjangService', () => {
let service: KeranjangService;
beforeEach(() => {
TestBed.configureTestingModule({ providers: [KeranjangService] });
service = TestBed.inject(KeranjangService);
});
it('menghitung total harga dengan benar', () => {
service.tambah({ id: 1, nama: 'Kopi', harga: 25000 } as Produk);
service.tambah({ id: 2, nama: 'Teh', harga: 15000 } as Produk);
expect(service.totalHarga()).toBe(40000);
expect(service.totalItem()).toBe(2);
});
});TestBed membuat lingkungan test terisolasi. Test di atas memverifikasi perilaku KeranjangService tanpa menyentuh UI — cepat, andal, dan tidak bergantung pada browser.
Pipe diuji dengan memanggil method transform langsung. Directive diuji dengan template yang memakainya:
import { RupiahPipe } from './rupiah.pipe';
describe('RupiahPipe', () => {
const pipe = new RupiahPipe();
it('memformat angka menjadi Rupiah', () => {
expect(pipe.transform(25000)).toContain('25.000');
});
});Test pipe cukup instansiasi class lalu panggil transform — tidak butuh TestBed. Directive dan guard membutuhkan setup kecil: buat host component untuk directive, dan panggil fungsi guard dengan stub dependency untuk memeriksa keputusan izin.
Guard adalah fungsi biasa, sehingga mudah diuji dengan fake AuthService:
import { TestBed } from '@angular/core/testing';
import { authGuard } from './auth.guard';
describe('authGuard', () => {
it('mengizinkan akses saat pengguna login', () => {
const hasil = TestBed.runInInjectionContext(() => authGuard());
expect(hasil).toBe(true);
});
});runInInjectionContext menjalankan guard dengan injector TestBed sehingga inject di dalamnya berfungsi. Untuk skenario belum login, berikan stub AuthService yang mengembalikan state berbeda.
Integration test menghubungkan beberapa bagian nyata — misalnya komponen dengan service nyata:
import { TestBed } from '@angular/core/testing';
import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting, HttpTestingController } from '@angular/common/http/testing';
import { PostService } from './post.service';
describe('PostService integration', () => {
let service: PostService;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
PostService,
provideHttpClient(),
provideHttpClientTesting(),
],
});
service = TestBed.inject(PostService);
httpMock = TestBed.inject(HttpTestingController);
});
it('memanggil endpoint yang benar', () => {
service.listar().subscribe();
const req = httpMock.expectOne('/api/posts');
expect(req.request.method).toBe('GET');
req.flush([]);
});
});HttpTestingController menangkap request yang keluar tanpa benar-benar mengirim ke jaringan — test berjalan cepat dan deterministik. req.flush([]) mensimulasikan respons server.
Unit dan integration test memverifikasi bagian-bagian; test end-to-end (E2E) memverifikasi seluruh alur pengguna di browser nyata — dari login, navigasi, hingga submit form.
npm install -D cypress
npx cypress openCypress dan Playwright sama-sama kuat. Protractor (pendahulu) sudah deprecated, jadi untuk project baru pilih salah satu dari keduanya.
describe('Alur login', () => {
it('berhasil masuk dan menuju dashboard', () => {
cy.visit('/login');
cy.get('[data-cy=email]').type('admin@example.com');
cy.get('[data-cy=password]').type('rahasia123');
cy.get('[data-cy=submit]').click();
cy.url().should('include', '/dashboard');
});
});Selector [data-cy=...] adalah atribut khusus test yang stabil — lebih baik daripada selector CSS yang sering berubah. E2E yang berjalan di CI memberikan keyakinan penuh bahwa alur utama aplikasi bekerja dari sudut pandang pengguna.
Inti yang harus dibawa pulang:
ng test menjalankan test Jasmine dengan Karma; file spec berdampingan dengan kode.TestBed.inject; komponen dengan fixture dan detectChanges.transform; guard dengan runInInjectionContext.HttpTestingController mensimulasikan respons tanpa jaringan nyata.data-cy membuat test E2E stabil terhadap perubahan UI.Di episode 17 selanjutnya kita akan membahas accessibility dan UX — atribut ARIA dan navigasi keyboard, markup semantik dan form yang aksesibel, responsive layout dan dukungan mobile, serta internationalization dan localization.