Home > Mobile >  Angular Unit Testing: Access the Observable @Input variable value passed from parent component
Angular Unit Testing: Access the Observable @Input variable value passed from parent component

Time:12-05

I'm new to testing. I'm writing a unit test for an Input variable which is being set in parent compoent. The value is an observable which use forkJoin.

parent.component.ts

public dataObj$: Observable<{mixType}>;

  public ngOnInit(): void {
    this.showChildComponent();
  }
private showChildComponent(): void {
    this.dataObj$ = forkJoin({
      userRoleObj: this.sessionService.sessionInfo$.pipe(take(1)),
      baseCountry: this.intlConfig.country$.pipe(take(1)),
      hasOrgEnabled: this.hasEnable$().pipe(take(1)),
      isAutoApproved: this.onlineService.isAutoApproved$().pipe(take(1)),
    });
  }

parent.template.html

<child-comp *ngIf="dataObj$ | async as data"  [data]="data" ></child-comp>

child.component.ts

export class ChildComponent implements OnInit {
  @Input data!: Observable<mixtype> 
  public settings: any;
  public show = false;
  
constructor() {}
  public ngOnInit(): void {
    this.settings = this.data; // I want data  to be accessible inside the test case
    this.setValidation();
  }

  public setValidation(): void {
    const isAdmin = this.settings.userRoleObj.profile.type === UI_CONSTS.IS_ADMIN ? true : false;
    if (
      this.settings.isAutoApproved &&
      isAdmin &&
      this.settings.baseCountry === UI_CONSTS.BASE_COUNTRY
    ) {
      this.show = true;
      this.setBulletText();
    } else {
      this.show = false;

    }
  }

  public setBulletText(): void {
    // some code to set the html content
  }

}

child.template.html

<div *ngIf="show">
    <div> .... </div>
    <h1> ...</h1>
</div>

child-component.spec.ts

const MOCK_IPD_SETTINGS: {
  baseCountry: string;
  isAutoApproved: boolean;
  userRoleObj: any;
  hasOrgEnabled: boolean;
} = {
 baseCountry: BASE_COUNTRY,
  isAutoApproved: true,
 userRoleObj: {
    profile: {
      type: '2',
    },
  },
 hasOrgEnabled: boolean;
};
describe('ChildComponent', () => {
  let component: ChildComponent;
  let fixture: ComponentFixture<ChildComponent>;

  function setUp({ MOCK_SETTINGS }) {
    component.data = of(MOCK_SETTINGS);
    console.log(component.data);
  }


  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        declarations: [ChildComponent],
        schemas: [CUSTOM_ELEMENTS_SCHEMA],
      }).compileComponents();
    }),
  );

  beforeEach(() => {
    fixture = TestBed.createComponent(InstaPayIpdComponent);
    component = fixture.componentInstance;
    // component.data = of(MOCK_SETTINGS);
    fixture.detectChanges();
  });


  it('onClickGetStarted', fakeAsync(() => {
    fixture.detectChanges();
    expect(component.show).toBe(true);
  }));
});

I'm writing unit test case for the child component. I'm not sure how to access the data or settings object inside the test case. I'm getting this error :

TypeError: Cannot read property 'profile' of undefined
``

CodePudding user response:

To access the Observable @Input variable value passed from a parent component in Angular unit testing, you can use the async() and fakeAsync() functions from the TestingModule.

For example, in your unit test file, you can import these functions and wrap your test case in the async() function like this:

import { async, fakeAsync } from '@angular/core/testing';

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

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

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

  it('should access the Observable @Input variable value passed from the parent component', fakeAsync(() => {
    // set the expected value for the Observable @Input variable
    const expectedValue = 'some value';

    // get the value of the Observable @Input variable
    const actualValue = component.myObservableInputVariable.getValue();

    // assert that the expected value matches the actual value
    expect(actualValue).toEqual(expectedValue);
  }));
});

Alternatively, you can also use the subscribe() method on the Observable @Input variable to access its value in the unit test. For example:

it('should access the Observable @Input variable value passed from the parent component', fakeAsync(() => {
  // set the expected value for the Observable @Input variable
  const expectedValue = 'some value';

  // create a variable to hold the actual value
  let actualValue;

  // subscribe to the Observable @Input variable and set the actual value
  component.myObservableInputVariable.subscribe((value) => (actualValue = value));

  // assert that the expected value matches the actual value
  expect(actualValue).toEqual(expectedValue);
}));

It is important to note that the async() and fakeAsync() functions are used to handle asynchronous operations in Angular unit tests, such as observables or promises. Therefore, it is necessary to wrap your test case in these functions to ensure that the test waits for the Observable @Input variable to emit a value before accessing it.

CodePudding user response:

You almost had it.

The first fixture.detectChanges() is when ngOnInit is called but the issue is you are setting it to an of and not the data itself.

In the HTML it is:

<child-comp *ngIf="dataObj$ | async as data"  [data]="data" ></child-comp>

The *ngIf is unwrapped as an observable but the [data] input is what the observable is unwrapped to.

// !! Pay attention to the lines with !!

beforeEach(() => {
    fixture = TestBed.createComponent(InstaPayIpdComponent);
    component = fixture.componentInstance;
    // !! Uncomment line below and remove of(...)
    component.data = MOCK_SETTINGS;
    fixture.detectChanges();
  });

You should hopefully not face that error any more.

  • Related