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.