Simple Analogies, Step-by-Step Code & Interview Questions (Zero Prior Testing Knowledge Required)
1. What is Unit Testing? (The Car Inspection Analogy)
Beginner Fundamentals
1.1 Key Testing Vocabulary Demystified
| Testing Term | Simple Explanation | Real Code Syntax |
|---|---|---|
| Jasmine / Jest | The Testing Library Framework that gives you test functions like describe(), it(), and expect(). |
Installed automatically in Angular projects (`spec.ts` files). |
| Karma | The Test Runner Tool that opens Chrome/Firefox in the background and runs all your tests. | Executed via command: npm test |
describe('Component', ...) |
A Test Suite Box that groups related tests together under a title. | describe('CalculatorService', () => { ... }) |
it('should add numbers', ...) |
An Individual Test Case that tests 1 specific scenario. | it('should return 4 when 2 + 2', () => { ... }) |
expect(result).toBe(4) |
The Assertion Check. Compares actual result with what you expect. If it matches, test passes GREEN ✅! | expect(total).toBe(100); |
2. How to Test a Simple Angular Service (Step-by-Step)
Service Unit Test
2.1 Testing a Calculator Service
Step 1: The Service Code (`calculator.service.ts`):
@Injectable({ providedIn: 'root' })
export class CalculatorService {
add(a: number, b: number): number {
return a + b;
}
}
Step 2: The Unit Test File (`calculator.service.spec.ts`):
describe('CalculatorService', () => {
let service: CalculatorService;
// beforeEach runs ONCE before every single test case to reset state!
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(CalculatorService);
});
it('should add 2 numbers correctly', () => {
const result = service.add(5, 10);
expect(result).toBe(15); // 👈 Assertion check!
});
});
3. Mocking Dependencies (`jasmine.createSpyObj`)
Mocking Masterclass
3.1 How to Mock an API Service in Angular Tests
describe('OffersComponent', () => {
let component: OffersComponent;
let mockOffersService: jasmine.SpyObj<OffersService>;
beforeEach(() => {
// 1. Create a Fake/Mock version of OffersService:
mockOffersService = jasmine.createSpyObj('OffersService', ['getOffers']);
// 2. Tell the mock what fake data to return when getOffers() is called:
mockOffersService.getOffers.and.returnValue(of([
{ id: 101, title: 'Discount Offer', amount: 500 }
]));
// 3. Provide the mock service inside TestBed:
TestBed.configureTestingModule({
providers: [
{ provide: OffersService, useValue: mockOffersService }
]
});
});
it('should load offers from service on init', () => {
component.ngOnInit();
expect(component.offers.length).toBe(1);
expect(mockOffersService.getOffers).toHaveBeenCalled(); // Checks if API was called!
});
});
4. Testing Asynchronous Code (`fakeAsync`, `tick`, `flush`)
Async Testing
4.1 The Fast-Forward Magic (`fakeAsync` & `tick`)
Problem: If your code has a `setTimeout(..., 3000)` or RxJS `debounceTime(300)`, running unit tests would take seconds or minutes to finish.
Solution: `fakeAsync()` creates a virtual clock zone! Calling `tick(3000)` fast-forwards 3 seconds instantly in 0 milliseconds!
it('should display success message after 2 second delay', fakeAsync(() => {
component.triggerDelayedSuccess(); // Has setTimeout 2000ms inside
expect(component.showMessage).toBeFalse(); // Message not visible yet!
tick(2000); // 👈 Fast-forwards virtual clock by 2000ms instantly!
expect(component.showMessage).toBeTrue(); // Message is now visible! ✅
}));
5. Change Detection & `NgZone.runOutsideAngular()`
Performance Engineering
5.1 The Performance Problem: High-Frequency Mouse Events
If you attach an event listener to mousemove or a 60 FPS Canvas Animation, the Detective triggers Angular Change Detection 60 times every second! This causes massive CPU lag and freezes the browser!
5.2 The Solution: `NgZone.runOutsideAngular()`
runOutsideAngular() tells Angular's Detective: "Ignore what I'm doing in this block! Do NOT run Change Detection!"
@Component({ ... })
export class HighPerformanceCanvasComponent implements OnInit {
private ngZone = inject(NgZone);
ngOnInit() {
// Tell Angular Detective to ignore mouse movements!
this.ngZone.runOutsideAngular(() => {
window.addEventListener('mousemove', (event) => {
// ⚡ High speed math calculation done smoothly at 60 FPS without Angular lag!
this.drawCanvasCursor(event.clientX, event.clientY);
});
});
}
// Only jump back inside Angular Zone when user actually saves data:
onSaveCanvas() {
this.ngZone.run(() => {
this.savedStatus = 'Saved successfully!'; // Updates HTML UI!
});
}
}
Post a Comment