How do you unit test if statements in angular

3 min read 06-10-2024
How do you unit test if statements in angular


Unit Testing If Statements in Angular: A Comprehensive Guide

Unit testing is crucial for ensuring the quality and reliability of your Angular application. One common challenge developers face is effectively testing conditional logic, particularly if statements. This article will guide you through the best practices for writing unit tests that cover your if statements thoroughly.

Understanding the Problem

Imagine you have an Angular component with a function that calculates a discount based on a user's purchase amount. The logic might look like this:

applyDiscount(amount: number): number {
  if (amount > 100) {
    return amount * 0.9; // 10% discount
  } else {
    return amount;
  }
}

The core issue is that you need to test both branches of the if statement – when the discount is applied (amount > 100) and when it's not.

Setting Up Your Unit Tests

Before diving into testing strategies, ensure you have the following:

  • Angular testing setup: Create a basic unit test file for your component.
  • Mock dependencies: If your component relies on external services or data, mock them using techniques like TestBed.configureTestingModule or Angular's MockProvider.

Strategies for Testing if Statements

1. Branch Coverage:

  • Goal: Execute each branch of the if statement separately.

  • Approach: Create two tests:

    • Test 1: Call the function with an input that satisfies the if condition (e.g., amount = 150).
    • Test 2: Call the function with an input that satisfies the else condition (e.g., amount = 50).
  • Example:

    import { Component } from '@angular/core';
    import { TestBed } from '@angular/core/testing';
    
    @Component({
      selector: 'app-discount-calculator',
      template: ''
    })
    class DiscountCalculatorComponent {
      applyDiscount(amount: number): number {
        if (amount > 100) {
          return amount * 0.9; // 10% discount
        } else {
          return amount;
        }
      }
    }
    
    describe('DiscountCalculatorComponent', () => {
      let component: DiscountCalculatorComponent;
    
      beforeEach(() => {
        TestBed.configureTestingModule({
          declarations: [DiscountCalculatorComponent],
        });
        component = TestBed.createComponent(DiscountCalculatorComponent).componentInstance;
      });
    
      it('should apply discount for amounts greater than 100', () => {
        const result = component.applyDiscount(150);
        expect(result).toBe(135); // Expected discount applied
      });
    
      it('should not apply discount for amounts less than or equal to 100', () => {
        const result = component.applyDiscount(50);
        expect(result).toBe(50); // Expected no discount
      });
    });
    

2. Using Mocking (For Complex Logic):

  • Goal: Test the behaviour of your component without worrying about external dependencies that might complicate the if statement logic.

  • Approach: Mock the dependency and provide a specific behaviour for the if statement based on your test case.

  • Example:

    import { Component } from '@angular/core';
    import { TestBed, ComponentFixture } from '@angular/core/testing';
    import { UserService } from '../user.service';
    
    @Component({
      selector: 'app-discount-calculator',
      template: ''
    })
    class DiscountCalculatorComponent {
      constructor(private userService: UserService) {}
    
      applyDiscount(amount: number): number {
        if (this.userService.isPremiumUser()) {
          return amount * 0.8; // 20% discount for premium users
        } else {
          return amount * 0.9; // 10% discount for regular users
        }
      }
    }
    
    describe('DiscountCalculatorComponent', () => {
      let component: DiscountCalculatorComponent;
      let userService: UserService;
      let fixture: ComponentFixture<DiscountCalculatorComponent>;
    
      beforeEach(() => {
        TestBed.configureTestingModule({
          declarations: [DiscountCalculatorComponent],
          providers: [
            {
              provide: UserService,
              useValue: {
                isPremiumUser: jasmine.createSpy('isPremiumUser').and.returnValue(false),
              },
            },
          ],
        });
        fixture = TestBed.createComponent(DiscountCalculatorComponent);
        component = fixture.componentInstance;
        userService = TestBed.inject(UserService);
      });
    
      it('should apply 10% discount for regular users', () => {
        const result = component.applyDiscount(150);
        expect(result).toBe(135); // Expected 10% discount
        expect(userService.isPremiumUser).toHaveBeenCalled(); // Ensure mock was called
      });
    
      it('should apply 20% discount for premium users', () => {
        // Configure the mock to return true for premium users
        userService.isPremiumUser.and.returnValue(true);
    
        const result = component.applyDiscount(150);
        expect(result).toBe(120); // Expected 20% discount
        expect(userService.isPremiumUser).toHaveBeenCalled(); // Ensure mock was called
      });
    });
    

Best Practices

  • Clear Test Descriptions: Use descriptive test names that clearly communicate the purpose of each test.
  • Avoid Overlapping Tests: Each test should focus on a specific aspect of the if statement logic. Avoid redundancy.
  • Isolate Tests: Mock dependencies to prevent external factors from interfering with your test results.
  • Use expect Assertions: Use appropriate expect assertions to verify the expected outcome of each test.
  • Maintain Code Coverage: Ensure your unit tests achieve high code coverage, indicating that all lines of your code are executed during testing.

Conclusion

Testing if statements is vital for creating robust Angular applications. By adopting the strategies outlined in this article, you can write comprehensive and reliable unit tests that ensure your conditional logic is working as intended. Remember, well-written tests are essential for catching bugs early and maintaining code quality over time.