Re-running Component Initialization Lifecycles in Angular Unit Tests
Tags: angular, typescript, jasmine, karma
Published: 2021-12-28 09:31:56

### Problem

In Angular unit testing, one common challenge is re-running a component's initialization lifecycles, specifically the `OnInit` lifecycle, to test different scenarios. Often, components behave differently based on certain conditions, and it's essential to test these scenarios to ensure proper functionality.

For example, let's consider a scenario where we want to display a login page or a home page based on the user's login status. The decision to show either page should happen during the component's initialization.

### Solution

When building Angular components using the Angular CLI, we can generate a component and a boilerplate unit test using `ng generate component MyComponent`. The generated unit test file typically looks like this:

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

import { MyComponent } from './my.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();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});
```

In this test setup, the key line is `fixture.detectChanges()`, which triggers the `OnInit` lifecycle in our component.

To test scenarios where our component behaves differently based on certain conditions, let's assume our component uses an `@Input()` property to determine the user's authorization status:

```typescript
@Component({
  selector: 'app-my-component',
  template: `
    <ng-container *ngIf="isLoggedIn; else notLoggedInTemplate">
      <app-home></app-home>
    </ng-container>
    <ng-template #notLoggedInTemplate>
      <app-authorization></app-authorization>
    </ng-template>
  `,
})
export class MyComponent implements OnInit {
  @Input() isLoggedIn: boolean;

  ngOnInit(): void {
    if (this.isLoggedIn) this.doSomethingBasedOnLogIn();
  }

  // Other component logic...
}
```

To test this component with different login scenarios, we need to create the component twice (inside the unit test) and pass in the `isLoggedIn` property accordingly. After setting the input, we run `fixture.detectChanges()` to trigger the component's initialization and change detection.

Here's how we can write our unit tests to cover these scenarios:

```typescript
describe('MyComponent', () => {
  let component: MyComponent;
  let fixture: ComponentFixture<MyComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [MyComponent, AuthorizationComponent, HomeComponent],
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  describe('Behaves correctly based on "isLoggedIn" status', () => {
    it('should display login component if not logged in', () => {
      component.isLoggedIn = false;
      fixture.detectChanges();

      const myComponent = fixture.debugElement.nativeElement as HTMLElement;
      expect(myComponent.querySelector('app-authorization')).toBeTruthy();
    });

    it('should display home component if already logged in', () => {
      component.isLoggedIn = true;
      fixture.detectChanges();

      const myComponent = fixture.debugElement.nativeElement as HTMLElement;
      expect(myComponent.querySelector('app-home')).toBeTruthy();
    });
  });
});
```

By setting the `isLoggedIn` property appropriately and re-running `fixture.detectChanges()` before each test, we can verify that our component behaves as expected under different conditions.

## Conclusion

In conclusion, we have learned how to effectively re-run a component's initialization lifecycles, specifically the `OnInit` lifecycle, in Angular unit tests. By using the `fixture.detectChanges()` method, we can trigger the initialization process and test our components under various scenarios.

We explored a scenario where our component's behavior depends on an `@Input()` property representing the user's login status. Through unit tests, we demonstrated how to pass different values to this property and verify that the component responds correctly based on the input.

This approach allows us to create robust and comprehensive tests for our Angular components, ensuring they behave as expected under different conditions.

With a solid understanding of re-running initialization lifecycles, you can confidently write unit tests that cover various use cases, leading to more reliable and maintainable Angular applications.

I hope you find this blog helpful for your Angular unit testing journey! Happy coding!