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);
fixture.detectChanges(); // final change
}
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ChildComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents();
}),
);
beforeEach(() => {
fixture = TestBed.createComponent(InstaPayIpdComponent);
component = fixture.componentInstance;
// component.data = of(MOCK_SETTINGS);
});
it('onClickGetStarted', fakeAsync(() => {
fixture.detectChanges();
expect(component.show).toBe(true);
}));
it('show open modal',() => {
setUp({...});
expect(component.isAutoApproved).toBe(false);
});
});
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
``
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.
Edit
Every time you change data
, you have to explicitly call ngOnInit
because we are setting this.settings = data;
there and we are reading this.settings
. It is not enough to just do component.data =
and expect this.settings
gets the update. So in short:
// We update the data
component.data = /* something new */
// Call ngOnInit so this.settings updates
component.ngOnInit();
// Carry on with your test