Angular unit testing interview questions and answers

Angular unit testing interview questions and answers 

1. What is unit testing in Angular?

Answer:
Unit testing in Angular refers to the practice of testing individual units of code (usually components, services, or other classes) in isolation to ensure that they behave as expected. Angular provides tools like Jasmine (testing framework) and Karma (test runner) to help with writing and running unit tests. Unit tests ensure that the code is modular, maintainable, and performs correctly under various conditions.


2. What are the tools commonly used for unit testing in Angular?

Answer:
The main tools used for unit testing in Angular are:

  • Jasmine: A behavior-driven development framework for testing JavaScript code. It provides functions like describe(), it(), and expect() to define and assert test cases.
  • Karma: A test runner that works with Jasmine and other testing frameworks. Karma runs the tests in a real browser or headless browser and provides results in the command line.
  • TestBed: A testing utility provided by Angular that allows you to configure and initialize the Angular environment for testing. It is used to create test modules and components.
  • Protractor: An end-to-end testing framework for Angular applications, but for unit testing, it is not typically used.

3. How do you test an Angular component?

Answer:
To test an Angular component, you typically use TestBed to configure the testing environment, create the component instance, and trigger change detection. Here’s an example:

import { ComponentFixture, TestBed } from '@angular/core/testing';

import { MyComponent } from './my-component.component';

 

describe('MyComponent', () => {

  let component: MyComponent;

  let fixture: ComponentFixture<MyComponent>;

 

  beforeEach(async () => {

    await TestBed.configureTestingModule({

      declarations: [ MyComponent ]

    })

    .compileComponents();

  });

 

  beforeEach(() => {

    fixture = TestBed.createComponent(MyComponent);

    component = fixture.componentInstance;

    fixture.detectChanges(); // triggers change detection

  });

 

  it('should create', () => {

    expect(component).toBeTruthy();

  });

});

  • TestBed configures the module for testing.
  • ComponentFixture is used to interact with the component instance and trigger change detection.

4. What is the purpose of async and fakeAsync in Angular testing?

Answer:

  • async: This function is used when you need to work with asynchronous operations (like HTTP requests or timers). It waits for all promises to resolve before running the test.
  • fakeAsync: Used for handling asynchronous code with Angular's zone.js. It allows you to simulate the passage of time and control asynchronous operations, such as setTimeout or Promise resolution, synchronously.

Example of using async:

it('should fetch data asynchronously', async () => {

  const data = await service.getData();

  expect(data).toBeDefined();

});

Example of using fakeAsync:

it('should call setTimeout', fakeAsync(() => {

  let value = 0;

  setTimeout(() => {

    value = 1;

  }, 1000);

  tick(1000); // Simulates the passage of 1000 ms

  expect(value).toBe(1);

}));

5. What is a spy in Jasmine, and how do you use it in Angular testing?

Answer:
A spy is a function that tracks calls to another function, allowing you to mock the behavior or verify that a function has been called. In Angular unit testing, spies are commonly used to mock services, HTTP requests, or other dependencies.

Example:

it('should call the fetchData method', () => {

  const service = jasmine.createSpyObj('MyService', ['fetchData']);

  service.fetchData.and.returnValue(of('mock data')); // Mock return value

 

  component.getData();

 

  expect(service.fetchData).toHaveBeenCalled();

});

  • createSpyObj() creates a mock object with a spy for each method you want to track.
  • and.returnValue() is used to mock the return value of the spied method.

6. What is the role of beforeEach() in Angular unit testing?

Answer:
beforeEach() is a Jasmine function that runs before each test in a describe() block. It is typically used for setup tasks like configuring modules, creating components, and initializing data. It ensures that the tests are isolated and have a clean state.

Example:

beforeEach(() => {

  // Initialize variables, mock services, or configure TestBed

  fixture = TestBed.createComponent(MyComponent);

  component = fixture.componentInstance;

});

7. What is the difference between shallow and deep testing in Angular?

Answer:

  • Shallow Testing: In shallow testing, only the component under test is created, and its child components are mocked or not rendered. It is faster and more isolated, focusing on testing the component itself without worrying about the child components.
  • Deep Testing: In deep testing, the entire component tree, including child components and templates, is rendered and tested. This is useful when you want to test the full integration of a component, including its interactions with child components.

Example of shallow testing:

TestBed.configureTestingModule({

  declarations: [ MyComponent ]

});

Example of deep testing:

TestBed.configureTestingModule({

  declarations: [ MyComponent, ChildComponent ]

});

8. How do you test HTTP requests in Angular?

Answer:
You can test HTTP requests by using the HttpClientTestingModule and HttpTestingController. This allows you to mock HTTP requests and verify that they are made correctly.

Example:

import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';

 

describe('MyService', () => {

  let httpMock: HttpTestingController;

  let service: MyService;

 

  beforeEach(() => {

    TestBed.configureTestingModule({

      imports: [ HttpClientTestingModule ],

      providers: [ MyService ]

    });

 

    service = TestBed.inject(MyService);

    httpMock = TestBed.inject(HttpTestingController);

  });

 

  it('should fetch data', () => {

    const mockData = { name: 'Test Data' };

 

    service.getData().subscribe(data => {

      expect(data).toEqual(mockData);

    });

 

    const req = httpMock.expectOne('http://api.example.com/data');

    expect(req.request.method).toBe('GET');

    req.flush(mockData);

  });

 

  afterEach(() => {

    httpMock.verify();

  });

});

  • expectOne() checks that there is only one HTTP request for the given URL.
  • req.flush() is used to provide a mock response to the HTTP request.
  • httpMock.verify() ensures no outstanding HTTP requests after the test.

9. How do you test form validation in Angular?

Answer:
To test form validation in Angular, you can interact with the form control properties and test their behavior based on user input. You can check whether the form is valid or invalid and whether the correct validation error messages appear.

Example:

it('should mark the form as invalid when required fields are empty', () => {

  component.form.controls['name'].setValue('');

  component.form.controls['email'].setValue('');

  expect(component.form.invalid).toBeTruthy();

});

10. How do you mock a service in Angular unit tests?

Answer:
In Angular unit tests, you can mock services using Jasmine spies or by creating a mock class. You can inject the mock service into the component to simulate the behavior of the real service.

Example using a spy:

it('should call the fetchData method of the service', () => {

  const mockService = jasmine.createSpyObj('MockService', ['fetchData']);

  mockService.fetchData.and.returnValue(of({ data: 'mock' }));

 

  const component = new MyComponent(mockService);

  component.ngOnInit();

 

  expect(mockService.fetchData).toHaveBeenCalled();

});

Example using a mock class:

class MockService {

  fetchData() {

    return of({ data: 'mock' });

  }

}

 

beforeEach(() => {

  TestBed.configureTestingModule({

    providers: [{ provide: MyService, useClass: MockService }]

  });

});

11. How do you test an Angular directive?

Answer:
Testing Angular directives involves setting up a testing module that declares the directive, then interacting with an element to check the expected behavior (e.g., DOM manipulation, event handling). You can use DebugElement to interact with the directive in the DOM.

Example:

it('should apply the correct class on mouseover', () => {

  const element = fixture.debugElement.query(By.css('div'));

  const nativeElement = element.nativeElement;

 

  // Simulate mouseover event

  nativeElement.dispatchEvent(new Event('mouseover'));

  fixture.detectChanges();

 

  // Check if the class is added

  expect(nativeElement.classList.contains('highlight')).toBe(true);

});

12. How do you test a pipe in Angular?

Answer:
Testing pipes in Angular is straightforward as you just pass input values through the pipe and check the output.

Example:

it('should transform the input value correctly', () => {

  const pipe = new MyCustomPipe();

  expect(pipe.transform('hello')).toBe('HELLO');

});

 

13. What is the difference between beforeEach and afterEach in Angular testing?

Answer:

·       beforeEach: This function is executed before each test case in a describe block. It is commonly used for setup tasks like initializing services, components, or mock data.

·       afterEach: This function is executed after each test case. It is typically used for cleanup tasks such as verifying that no HTTP requests are left pending or clearing up mock data.

Example:

beforeEach(() => {

  // Setup tasks (e.g., creating component, initializing services)

});

 

afterEach(() => {

  // Cleanup tasks (e.g., verifying HTTP requests, resetting spies)

});

14. What is the purpose of TestBed.configureTestingModule() in Angular unit testing?

Answer:
TestBed.configureTestingModule() is used to configure the testing environment for your unit tests. It is where you define the components, services, and other dependencies that will be used in the tests. This method prepares everything that Angular needs to instantiate the components or services during testing.

Example:

beforeEach(() => {

  TestBed.configureTestingModule({

    declarations: [MyComponent],  // Declare components used in the test

    providers: [MyService]        // Provide services or dependencies

  });

});

15. How do you test a service method that makes an HTTP call in Angular?

Answer:
You can use the HttpClientTestingModule and HttpTestingController to mock HTTP requests and verify that they are being made correctly. The HttpTestingController allows you to intercept the HTTP requests and provide a mock response.

Example:

import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';

 

describe('MyService', () => {

  let service: MyService;

  let httpMock: HttpTestingController;

 

  beforeEach(() => {

    TestBed.configureTestingModule({

      imports: [HttpClientTestingModule],

      providers: [MyService]

    });

    service = TestBed.inject(MyService);

    httpMock = TestBed.inject(HttpTestingController);

  });

 

  it('should fetch data via HTTP GET', () => {

    const mockData = { name: 'John' };

 

    service.getData().subscribe(data => {

      expect(data).toEqual(mockData);

    });

 

    const req = httpMock.expectOne('http://api.example.com/data');

    expect(req.request.method).toBe('GET');

    req.flush(mockData);

  });

 

  afterEach(() => {

    httpMock.verify();  // Verify that there are no outstanding requests

  });

});

16. What is the role of tick() in Angular testing, and when should it be used?

Answer:
tick() is used in conjunction with fakeAsync to simulate the passage of time for asynchronous tasks (such as setTimeout, setInterval, or promises). It allows you to advance the virtual clock by a specified number of milliseconds, triggering the completion of timers or delayed code.

Example:

it('should call setTimeout after 1 second', fakeAsync(() => {

  let value = 0;

  setTimeout(() => { value = 1; }, 1000);

  tick(1000);  // Simulate passage of time

  expect(value).toBe(1);

}));

When to use tick(): Use it when you need to handle asynchronous operations that depend on timers, such as setTimeout or setInterval.

17. What is the difference between ComponentFixture.detectChanges() and ComponentFixture.whenStable() in Angular testing?

Answer:

·       detectChanges(): It triggers Angular's change detection and updates the component’s view. You use this when you want to explicitly update the view after changing the state of the component (e.g., updating properties).

·       whenStable(): It is an asynchronous method that waits for all asynchronous tasks (such as HTTP requests or setTimeout calls) to finish. You typically use this method when you have asynchronous operations in your component that affect the view.

Example:

it('should update the view after async operation', async(() => {

  component.fetchData();

  fixture.whenStable().then(() => {

    fixture.detectChanges();

    expect(component.data).toBeDefined();

  });

}));

Use detectChanges() when you want to trigger change detection manually. Use whenStable() to wait for asynchronous operations to complete.

18. How do you test custom Angular pipes?

Answer:
To test custom pipes, you can directly instantiate the pipe and test its transformation method. You can pass different input values and assert the output.

Example:

import { MyCustomPipe } from './my-custom.pipe';

 

describe('MyCustomPipe', () => {

  let pipe: MyCustomPipe;

 

  beforeEach(() => {

    pipe = new MyCustomPipe();

  });

 

  it('should transform value correctly', () => {

    expect(pipe.transform('test')).toBe('TEST');

  });

});

This tests the transform() method of the custom pipe and ensures it behaves as expected.

19. How do you test an Angular component’s lifecycle methods (e.g., ngOnInit, ngOnChanges)?

Answer:
You can test Angular component lifecycle methods by invoking them and checking their behavior. For example, to test ngOnInit, you can trigger it by creating a component instance and calling detectChanges() to execute Angular's lifecycle hooks.

Example:

it('should call ngOnInit', () => {

  const spyOnInit = spyOn(component, 'ngOnInit');

  component.ngOnInit();

  expect(spyOnInit).toHaveBeenCalled();

});

For ngOnChanges, you can pass in @Input() values and check if the component reacts appropriately.

Example:

it('should call ngOnChanges when input changes', () => {

  const spyOnChanges = spyOn(component, 'ngOnChanges');

  component.someInput = 'new value';

  component.ngOnChanges();

  expect(spyOnChanges).toHaveBeenCalled();

});

20. What is the difference between shallow and deep testing in Angular components?

Answer:

·       Shallow Testing: In shallow testing, you only test the component itself without rendering or testing its child components. This isolates the unit of code and tests it independently.

·       Deep Testing: In deep testing, the component and its entire hierarchy (including child components) are rendered and tested. This allows you to test how the component interacts with its children and dependencies.

Example of shallow testing:

TestBed.configureTestingModule({

  declarations: [MyComponent],  // Only the component under test

});

Example of deep testing:

TestBed.configureTestingModule({

  declarations: [MyComponent, ChildComponent],  // Includes child components

});

21. How do you test a component with router dependencies?

Answer:
To test components that depend on the Angular Router, you can use RouterTestingModule to mock the router functionality. This allows you to simulate navigation, query parameters, and route activation.

Example:

import { RouterTestingModule } from '@angular/router/testing';

 

beforeEach(() => {

  TestBed.configureTestingModule({

    imports: [RouterTestingModule],  // Mock the Router module

    declarations: [MyComponent]

  });

});

 

it('should call navigate on button click', () => {

  const router = TestBed.inject(Router);

  const spyNavigate = spyOn(router, 'navigate');

  component.someAction();

  expect(spyNavigate).toHaveBeenCalledWith(['someRoute']);

});

This tests if the component interacts with the router correctly.

22. What is the role of inject() in Angular testing?

Answer:
inject() is a utility function provided by Angular's testing module to retrieve an instance of a service or other injectable class during a unit test. It is commonly used in combination with TestBed to get access to services for testing.

Example:

it('should call the fetchData method of MyService', inject([MyService], (service: MyService) => {

  spyOn(service, 'fetchData').and.returnValue(of('mock data'));

  component.fetchData();

  expect(service.fetchData).toHaveBeenCalled();

}));

inject() is useful when you need to access a service or dependency directly within a test case, especially if you're not setting up a full TestBed for the test.

23. How do you test Angular Forms (Reactive Forms or Template-Driven Forms)?

Answer:
Testing Angular forms involves testing both form controls and form validation. For Reactive Forms, you can directly access form controls in the component. For Template-Driven Forms, you can use fixture.detectChanges() and fixture.debugElement to interact with the form in the template.

Example for Reactive Forms:

it('should mark form as invalid when required fields are empty', () => {

  component.form.controls['name'].setValue('');

  component.form.controls['email'].setValue('');

  fixture.detectChanges();

  expect(component.form.invalid).toBeTrue();

});

Example for Template-Driven Forms:

it('should submit form when valid', () => {

  const form = fixture.debugElement.query(By.css('form')).nativeElement;

  form.controls['username'].value = 'user';

  form.controls['password'].value = 'password';

  form.submit();

  expect(component.onSubmit).toHaveBeenCalled();

});

 


Comments

Popular posts from this blog

Multiline to singleline IN C# - CODING

EF Core interview questions for beginners

EF Core interview questions for experienced