Reputation: 2463
I have a service SmsService
that returns a promise within the submit()
method in my component.
In my component I call the this.sms.send()
method from the SmsService and append then()
& catch()
: this.sms.send(this.messageInput).then(....).catch(....)
The assertion of expect(smsSpy.send).toHaveBeenCalledWith(component.messageInput)
works correctly, after this.sms.send()
is called, However this.messageInput
is not reset to null and that assertion fails. What am I doing wrong? Am I not running change detection properly?
I'm trying to write a couple tests to ensure that then
and catch
within my component are each called depending on the Promise returned from my send()
spy. The How would I do this? .
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import {MomentModule} from 'angular2-moment'
import { FormsModule } from '@angular/forms';
import { AngularFirestore } from '@angular/fire/firestore';
import { Observable, of } from "rxjs";
import { ActivatedRoute } from '@angular/router';
import {SmsService} from './sms.service'
//my component
import {
Component,
OnInit,
AfterViewChecked,
ViewChild,
ElementRef,
AfterViewInit
} from "@angular/core";
@Component({
selector: "app-sms",
templateUrl: "./sms.component.pug",
styleUrls: ["./sms.component.scss"]
})
export class SmsComponent {
@ViewChild("scrollMe") private myScrollContainer: ElementRef;
public messageInput;
public list;
public err;
constructor(public sms: SmsService, private route:ActivatedRoute) {
this.list = this.sms.getItems()//this.route.data['value']['messages']
}
submit() {
this.sms
.send(this.messageInput)
.then(res => {
// console.log(res);
this.messageInput = null
})
.catch(err => {
this.err = `Could not send - ${err}`
});
}
}
//my test
describe('SmsComponent', () => {
let component: SmsComponent;
let fixture: ComponentFixture<SmsComponent>;
var afSpy = jasmine.createSpyObj('AngularFirestore', ['collection', 'valueChanges', 'snapshotChanges', 'pipe', 'add']);
var smsSpy = jasmine.createSpyObj('SmsService', ['send', 'then','catch']);
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SmsComponent ],
providers:[
{ provide: SmsService, useValue: smsSpy },
{ provide: AngularFirestore, useValue: afSpy },
{
provide: ActivatedRoute, useValue: {
params: of([{ id: 'test' }])
}
}
],
imports:[MomentModule, FormsModule],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SmsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('expect err', () => {
smsSpy.send.and.returnValue(Promise.resolve('asdf'));
smsSpy.catch.and.returnValue('caught');
component.messageInput = 'my new message'
component.submit()
fixture.detectChanges();
expect(smsSpy.send).toHaveBeenCalledWith(component.messageInput) //<-- this passes
expect(component.messageInput).toBe(null) //<---- this fails
});
});
Upvotes: 0
Views: 1824
Reputation: 692231
Your service doesn't have any then
or catch
method. It only has a send()
method, which returns a Promise. The returned Promise is the object which has a then and a catch method. So this line makes little sense:
var smsSpy = jasmine.createSpyObj('SmsService', ['send', 'then','catch']);
You need to make the spy return a promise, and you need to take the control of the asynchrony, for example by using fakeAsync()
.
Here's a complete example almost matching yours (I only kept the important part):
Component:
import { Component } from '@angular/core';
import { SmsService } from '../sms.service';
@Component({
selector: 'app-sms',
templateUrl: './sms.component.html',
styleUrls: ['./sms.component.scss']
})
export class SmsComponent {
public messageInput: string;
constructor(private sms: SmsService) { }
submit() {
this.sms
.send(this.messageInput)
.then(res => {
this.messageInput = null;
});
}
}
Test:
import { fakeAsync, TestBed, tick } from '@angular/core/testing';
import { SmsComponent } from './sms.component';
import { SmsService } from '../sms.service';
describe('SmsComponent', () => {
let component: SmsComponent;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ SmsComponent ]
});
});
beforeEach(() => {
component = TestBed.createComponent(SmsComponent).componentInstance;
});
it('should sublut and clear', fakeAsync(() => {
// obtain the service injected in the component by Angular
const smsService: SmsService = TestBed.get(SmsService);
// spy on its send method, and make it return a resolved promise
spyOn(smsService, 'send').and.returnValue(Promise.resolve('hello world'));
// set the message input in the component
component.messageInput = 'world';
// call submit()
component.submit();
// check that the service call has been made
expect(smsService.send).toHaveBeenCalledWith('world');
// tick in order to trigger the execution of the then callback registered on the promise
tick();
// check that the callback has rset the messageInput to null
expect(component.messageInput).toBeNull();
}));
});
Upvotes: 1