Angular Karma JUnit test - SpyOn does not work within private method - javascript

I try to test my component, I know the component works fine but my test gives error since Angular version has been updated to 12.
This is my component:
ngOnInit() {
if (versonA) {
this.doThis();
} else {
this.doThat();
}
}
private doThis() {
this.myService.confirm({
message: message,
accept: () => {
this.doAcceptLogic();
}, reject: () => {
console.log('reject')
this.doRejectLogic();
}
});
}
And this is my test:
beforeEach(async () => {
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
fixture.autoDetectChanges();
spyOn(TestBed.get(MyService), 'confirm').and.callFake((params: Confirmation) => {
params.reject();
});
await fixture.whenStable();
});
And still, this spyOn does not seem to work.
I put a lot of console logs into my code and the doThis() method still get called but the log within my confirm method ('reject') does not get written to the console.
I cannot see why.
As I change the doThis() method to public and call it directy from my test as component.doThis(), then it runs to into the mocked myService.
Can anybody explain my why?
Thanks a lot!

You can call private methods in your spec either by casting to any
(component as any).doThis();
or using []
component['doThis']();
You call fixture.autoDetectChanges() it may call ngOnInit which is calling doThis before even you created the spy.

Related

How do I mock a primitive value for a single test?

I have a test that requires the value of isBanana to be false.
The test works (and isBanana is false) when I mock the function in the top level index.test.js. However this breaks other tests (as they require isBanana to be true).
jest.mock("myapp-api-functions", () => {
console.log(`executing mock function`);
return {
...jest.requireActual("myapp-api-functions"),
isBanana: false,
};
});
If I move the jest.mock() into the body of the test, isBanana is true and the test doesn't work.
it(`should error when someone tries to use the mock account in production`, async () => {
jest.mock("myapp-api-functions", () => {
console.log(`executing mock function`);
return {
...jest.requireActual("myapp-api-functions"),
isBanana: false,
};
});
...same test function that previously passed...
});
The mock doesn't work and the test fails.
How can I mock the primitive value for a single test?
Calls to jest.mock get hoisted to the top of the code block.
To avoid this behaviour you can instead use jest.doMock e.g.
it(`should error when someone tries to use the mock account in
production`, async () => {
jest.doMock("myapp-api-functions", () => {
console.log(`executing mock function`);
return {
...jest.requireActual("myapp-api-functions"),
isBanana: false,
};
});
// Same test function that previously passed...
});
This will allow you to specify mock behaviour for a specific test.

Trying to subscribe to a http call in test file, and i need to assert the observerable in my test

I need to make a HTTP call in my test and I need to verify my observeable.
When I debug the code, the HTTP call is getting called in service, but in my test, it fails and it says http is undefined, but while debugging I'm able to see the http observal in console.
//service.ts file
//imports modles goes here
export class DataService {
private api = this.configService.config.apiUrl;
constructor(private http: HttpClient,
private configService: AppConfigService,
private errorService: ErrorService)
{
}
public getCustomerList(): Observable<IUserResponse> {
return this.http.get<IUserResponse>`${this.api}/v1/getusers/users`);
}
}
my test file serviec.specs.ts
describe('OnboardingService', () => {
beforeEach(() => {
const httpClient = new HttpClient(new HttpXhrBackend({ build: () => new XMLHttpRequest() }));
appConfigService= new AppConfigService(httpClient);
appConfigService.config = { apiUrl:"https://test-test-getuser.json"}
erroService = new ErrorService();
service = new Dataservice(httpClient, appConfigService,erroService);
it('should return an Observable<Iuser[]>', done => {
service.getCustomerList().subscribe(response => {
expect(response.users).toBeGreaterThan(0);
done();
});
});
})
Try this
this.http.get(`${this.apiurl}/v1/getuser`).subscribe((response)=>{console.log(response.user)})
Grey right arrow shows your code to execute. Left grey arrow below shows the result of execution. Console log has no arrow in dev tools.
The result of execution .subscribe() is a Subscriber as you can notice in your screenshot. Your code just doesn't execute function you passed to .subscribe().
So your problem is with your endpoint probably. Check the network tab in your dev tools.

How can I get a property attached to a global to stub properly with sinon?

I have a helper.js that loads before my tests:
before(async function() {
this.timeout(30000)
global.db = require(`${process.cwd()}/models`)()
...
Then in my test, I have:
describe.only('Phone Library', () => {
let updateCallSpy
beforeEach(() => {
sinon.stub(twilioClient.calls, 'create').resolves({})
updateCallSpy = sinon.stub(global.db.models.Call, 'update').resolves(true)
// sinon.stub(global.db.models.Conversation, 'update').resolves({})
})
The twilioClient.calls.create stubs properly. But the global.db.models.Call.update does not.
In my actual code, I'm using it like:
await global.db.models.Call.update({ status: newCallStatus }, { where: { id: foundConversation.Call.id } })
When I console.log(global.db.models.Call), it simply outputs Call. However, the .update function is there and does what it's supposed to do (a Sequelize model that updates).
I'm sure It's something terribly obvious, so any pointers would be greatly appreciated. Thanks.
The sequelize model methods are defined as prototype by the sequelize core.
The following should work
updateCallSpy = sinon.stub(global.db.models.Call.prototype, 'update').resolves(true)
You can also create a stub instance:
updateCallStubInstance = sinon.createStubInstance(global.db.models.Call)

Subscribing to a change, but needing to see variable from where Service is used?

My code has been refactored and some extracted into a service that subscribes to functions. However, my original code had a call within the subscription that referenced a variable within the file, but now I'm not sure how to best reach it?
I am struggling with where to place the line:
this.select.reset('some string'); found within the subscribeToMessageService() function.
Original code
event.component.ts
select: FormControl;
#ViewChild('mySelect') mySelect: ElementRef;
subscribeToMessageService() {
this.messageService.serviceMsg
.subscribe(res => {
// unrelated code
this.select.reset('some string');
});
}
subscribeToEventService() {
this.eventService.eventSubject
.subscribe(res => {
this.select = new FormControl(res.status);
this.select.valueChanges.subscribe(value => {
// manual blurring required to stop error being thrown when popup appears
this.selector.nativeElement.blur();
// do something else
});
});
}
Refactored code
status.service.ts
subscribeToMessageService(): void {
this.messageService.serviceMsg
.subscribe(res => {
// unrelated code
// This is where 'this.select.reset('some string');' would have gone
});
}
status.component.ts
select: FormControl;
#ViewChild('exceptionalSelect') selector: ElementRef;
subscribeToEventService() {
this.eventService.eventSubject
.subscribe(res => {
this.select = new FormControl(res.status);
this.select.valueChanges.subscribe(value => {
// manual blurring required to stop error being thrown when popup appears
this.selector.nativeElement.blur();
this.onStatusChange(value);
});
});
}
Since you still want to subscribe to the original source messageService.serviceMsg your new StatusService needs to expose this observable to the injecting component (StatusComponent).
This can be done for example by creating a public observable in the StatusService (possibly by utilising rxjs Subject or angular EventEmitter) and triggering the emit in the subscription of messageService.serviceMsg.
Then your StatusComponent only needs to inject StatusService and do
this.statusService.serviceMsg // <-- might choose another name to make clear that this is passed on.
.subscribe(res => {
// unrelated code
this.select.reset('some string');
});

Angular - Unit testing Subject()?

I have a Angular service that simply uses a Subject, but I am unsure of how to write unit tests for it.
I saw [this thread][1], but I didn't find it terribly helpful.
I have attempted to mock next() but I am more than a little lost.
You should spy on service.serviceMsg and not service, because next() method appears to be on serviceMsg subject.
it('should catch what is emitted', () => {
const nextSpy = spyOn(service.serviceMsg, 'next');
service.confirm(ACTION);
expect(nextSpy).toHaveBeenCalled();
});
EDIT :
You should also change the way you are creating service instance. What you show in your code is applicable only for component instance creation
beforeEach(() => {
TestBed.configureTestingModule({
providers: [MessageService]
});
service = TestBed.get(MessageService); // get service instance
httpMock = TestBed.get(HttpTestingController);
});
Firstly you can just subscribe to your Subject and inside expect some value and after that just execute method which will emit that:
it('should catch what is emitted', () => {
service.serviceMsg.subscribe(msg => {
expect(msg).toEqual(something);
});
service.confirm(value); // this should match what you expect above
});

Categories