Mocked Service's returned observable is undefined? - unit-testing

I am testing an angular component, to ensure it calls a service correctly. I am mocking the service as such:
class mockSocket {
getComments() { return Observable.of(['true', 'false'])}
};
describe('CommentTableComponent', () => {
let component: CommentTableComponent;
let fixture: ComponentFixture<CommentTableComponent>;
let mockSock = new mockSocket();
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [CommentTableComponent],
providers: [
{ provide: SocketService, useValue: mockSock }
imports: [Ng2SmartTableModule]
})
.compileComponents();
}));
In my component's ngOnInit event, the getComments function should be called and subscribed to -
ngOnInit() {
this.socketService.getComments('api/jobs/' + line).subscribe(comment => {
// some logic
}
However, the ngOnInit is throwing error
TypeError: Cannot read property 'subscribe' of undefined
in the tests - any idea why, I am returning an Observable in the standard way?

Related

Angular8 Unittest Router Events

I am trying to test my router event this is what te code in the TS file look like:
constructor(
private router: Router
) {
router.events.subscribe(route => {
// I removed the code because it doesn`t matter for the solution
});
}
unittest:
describe('MainComponent', () => {
let methodSpy: jasmine.Spy;
const eventSubject = new ReplaySubject<RouterEvent>(1);
const routerMock = {
navigate: jasmine.createSpy('navigateByUrl'),
navigateByUrl: jasmine.createSpy('navigateByUrl'),
events: eventSubject.asObservable(),
url: 'test/url',
createUrlTree: (commands, navExtras = {}) => {},
serializeUrl: (commands, navExtras = {}) => {}
}
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule, FormsModule],
declarations: [],
providers: [
{ provide: Router, useValue: routerMock},
{ provide: ActivatedRoute, useValue: routerMock},
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MainComponent);
component = fixture.componentInstance;
methodSpy = spyOn(component, 'AdminPanelStarted');
//fixture.detectChanges();
});
it('should trigger the router event and hit the function "Panel"', () => {
eventSubject.next(new NavigationEnd(1, 'test', 'routeUrl'));
expect(methodSpy).toHaveBeenCalled();
});
});
this is the error I am getting:
I can`t find the solution. The only thing I want to test is if the correct function have been called after entering the router.events subscription. The observable is triggered but gives the error that startsWith can not be done on undefined. But what is undefined?
Thx a Lot!

How to handle bootstrap-daterangepicker in angular component unit test?

I am trying to write a unit test of an angular 6 component which is initializing the bootstrap-daterangepicker in the ngAfterViewInit() method. When I run my unit test it gives the following error:
TypeError: $(...).daterangepicker is not a function
this is the code from the actual component(EmployeeComponent):
ngAfterViewInit(): void {
this.initializeDatePicker(this);
}
initializeDatePicker(that: any) {
const start = moment().subtract(7, 'days');
const end = moment();
$('#reportrange').daterangepicker({
startDate: start,
endDate: end,
maxDate: moment(),
ranges: {
'Today': [moment(), moment()],
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')]
}
}, cb);
cb(start, end);
}
this is the code from my test class:
describe('EmployeeComponent', () => {
let component: EmployeeComponent;
let fixture: ComponentFixture<EmployeeComponent>;
let messageService: NotificationService;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [EmployeeComponent]
})
.overrideComponent(EmployeeComponent, {
set: {
template: '',
providers: [
{ provide: NotificationService, useValue: messageService },
{ provide: ActivatedRoute, useValue: { queryParams: of({ emp: "123" }) } }
]
}
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EmployeeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
You don't need to handle it in your test cases. That component should be initialized in a separate service and you can simply mock that method from the service. In the way you can avoid this error.
let say you move all the code of the initializeDatePicker() in a method in some service let say common-service.ts and you can simply call that service from this method like
this.commonServiceObj.initializeDatePicker();
Now after doing this, you can simply mock initializeDatePicker() from the service object and error should be gone.

Testing Observable based call hierarchy in angular

I am trying to test a component which uses Observables and then cascades through several function calls when the Observable resolves. Here is a version of the component.
export class NotificationComponent implements OnInit {
private answerSubscription: Subscription;
constructor(public toasterService: ToasterService, private commentService: CommentService) { }
ngOnInit() {
this.answerSubscription = this.commentService.answer$.subscribe(
answer => this.commentComplete(answer));
}
commentComplete(answer) {
this.toasterService.clear(answer.toastId);
let promptAns = this.search(answer.toastId);
}
}
and here is my test:
class MockToastService {
clear() {}
}
class MockCommentService {
answer$: Observable<any>;
constructor() {
this.answer$ = Observable.of({toastId: '123'});
}
}
describe('NotificationComponent', () => {
let component: NotificationComponent; let fixture: ComponentFixture<NotificationComponent>;
let mockComment = new MockCommentService(); let mockToast = new MockToastService();
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [NotificationComponent, MockToast],
providers: [{ provide: ToasterService, useValue: mockToast },
{ provide: CommentService, useValue: mockComment }]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(NotificationComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should complete notification on answer', () => {
spyOn(component, 'commentComplete'); spyOn(mockToast, 'clear');
expect(component.commentComplete).not.toHaveBeenCalled();
component.ngOnInit();
expect(component.commentComplete).toHaveBeenCalled();
expect(mockToast.clear).toHaveBeenCalled();
});
});
The test passes on expect(component.commentComplete).toHaveBeenCalled();, but fails on expect(mockToast.clear).toHaveBeenCalled(). As you can see from the component, toasterService.clear( should be called straight after commentComplete, however, I have stepped through with a debugger, and the test criteria is being checked before the clear function is being called.
I have tried adding fakeAsync and tick(), but am still facing the issue. Any idea how I can make this test's timing work?
You should use fake Async here but as understand there the issues was not with it.
You fake 'commentComplete' function by spyOn(component,'commentComplete') but you need to spy and do its job. change to 'spyOn(component, 'commentComplete').and.callThrough();'
Spies: and.callThrough. By chaining the spy with and.callThrough, the spy will still track all calls to it but in addition it will delegate to the actual implementation.
https://jasmine.github.io/2.0/introduction.html
here is the code that should work:
it('should complete notification on answer', fakeAsync(() => {
const spyComplete = spyOn(component, 'commentComplete').and.callThrough();
const spyToast = spyOn(mockToast, 'clear');
expect(component.commentComplete).not.toHaveBeenCalled();
component.ngOnInit();
tick();
expect(spyComplete).toHaveBeenCalled();
expect(spyToast).toHaveBeenCalled();
}));

Angular 2 Unit Testing prepare mocking to share in components

I'm doing unit test in my Angular2 application and very new to angular2 test framework.
I have 2 components and calling same method from a service.
Firstly, I need to import the service and model to create the mock
import { MyService } from '../../shared/my.service';
import { MyModel } from '../../shared/my.model';
Secondly, arrange the model and service to mock
describe('MyComponent', () => {
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>;
const mockMyModel: MyModel[] = [];
const mockMyService = {
sameMethod: () => Observable.of(mockMyModel),
};
Finally, mock the service
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ MyComponent],
})
.overrideComponent(MyComponent, {
set: {
providers: [
{ provide: MyService , useValue: mockMyService },
],
},
})
.compileComponents();
}));
I need to do the same thing again for my another component.
Option 1: is there any way to globally share the preparation for mock
e.g. { provide: MyService , useValue: global.mockMyService }
Option 2: is it possible to have 'ServiceTestingModule' such as 'RouterTestingModule' just to mock a service.
Thanks!
Option 1 definitely can be used
myMockProvider = { provide: MyService , useValue: mockMyService };
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ MyComponent],
})
.overrideComponent(MyComponent, {
set: {
providers: [myMockProvider],
},
})
.compileComponents();
}));
myMockProvider can also be a list of providers. Nested lists are resolved/flattened by providers: [...] automatically.

Angular2 final version: Injected Service method under unit test returning undefined

I am trying to write some unit-tests on a component that got some services injected into it, to load the data from server. Data is loaded in this component on OnInit() method. I am trying that service method returns some dummy data, using spyOn. Following is unit-test setup -
let comp: MyComponent;
let fixture: ComponentFixture<MyComponent>;
let staticDataService: any;
let spy: jasmine.Spy;
let allCountries: string[];
describe('MyComponent', () => {
beforeEach( async(() => {
TestBed.configureTestingModule({
imports : [ FormsModule, HttpModule ],
declarations : [MyComponent],
providers: [ StaticDataService ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MyComponent);
comp = fixture.componentInstance;
staticDataService = fixture.debugElement.injector.get(StaticDataService);
allCountries = [] = ["US", "UK"];
spy = spyOn(staticDataService, 'getCountries').and.returnValue(Promise.resolve(allCountries));
});
it('Countries should be set', () => {
expect(comp.allCountries).toEqual(allCountries);
});
});
Following is the component class that I am unit-testing -
#Component({
moduleId: module.id,
selector: 'myeditor',
templateUrl: 'my.component.html',
styleUrls: ['my.component.css']
})
export class MyComponent implements OnInit {
allCountries: string[];
constructor(private _staticDataServices: StaticDataService) {}
ngOnInit() {
this.getDataFromServer();
}
getDataFromServer()
{
this.allCountries = this._staticDataServices.getCountries();
}
I am getting the following error -
Chrome 53.0.2785 (Windows 7 0.0.0) MyComponent Countries should be set FAILED
[1] Expected undefined to equal [ 'US', 'UK' ].
Under the same unit-tests few other tests are working fine, that are not dependent on injected services. Getting 'undefined' while testing the properties that are set by services.
Can someone please help what I am doing wrong here?
Thanks
You need to call fixture.detectChanges() for the ngOnInit to be called.
fixture = TestBed.createComponent(MyComponent);
fixture.detectChanges();
getCountries returns a Promise so you need to then it, otherwise the value of allCountries will just be promise and not the data
getDataFromServer() {
this._staticDataServices.getCountries().then(data => {
this.countries = data;
});
}
Since the promise is asynchronous, you need to use async and wait for the asynchronous task to complete by calling fixture.whenStable()
import { async } from '#angular/core/testing';
it('...', async(() => {
fixture.whenStable().then(() => {
expect(comp.allCountries).toEqual(allCountries);
})
})
UDPATE
Without seeing the StaticDataService, I'm guessing you are trying to inject Http into it. This wont work in a test environment without further configuration. What I suggest you do is just make the service a mock
staticDataService = {
getCountries: jasmine.createSpy('getCountries').and.returnValue(...);
}
providers: [
{ provide: StaticDataService, useValue: staticDataService }
]