I am trying to test a component that has an injected service. I want to provide a mock service in my test. However the test is using the original service instead of the mock one (I know this because I am getting a "No provider for HttpClient!" error and also I have a console.log in the original service that is outputting in the test).
I can fix the error by importing the HttpClientTestingModule but this doesn't fix the fact that the original service is being used instead of the mock one.
Any ideas what I am doing wrong?
Here is my test code. Angular version 7
import { TestBed, async, ComponentFixture } from '@angular/core/testing';
import { HelloWorldComponent } from '../../app/components/hello-world/hello-world.component';
import { HelloWorldService } from '../../app/services/hello-world.service';
describe('HelloWorldComponent', () => {
let component: HelloWorldComponent;
let fixture: ComponentFixture<HelloWorldComponent>;
let mockHelloWorldService;
beforeEach(() => {
mockHelloWorldService = jasmine.createSpyObj(['getHelloWorld']);
TestBed.configureTestingModule({
imports: [],
declarations: [HelloWorldComponent],
providers: [
[{ provide: HelloWorldService, useClass: mockHelloWorldService }]
]
}).compileComponents();
fixture = TestBed.createComponent(HelloWorldComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
UPDATE
I have tried overrideProvider and now I am now getting a 'Cannot read property 'subscribe' of undefined' error, which sort of feels like progress...
Here's my test code
import { TestBed, async, ComponentFixture } from '@angular/core/testing';
import { HelloWorldComponent } from '../../app/components/hello-world/hello-world.component';
import { HelloWorldService } from '../../app/services/hello-world.service';
import { of } from 'rxjs';
describe('HelloWorldComponent', () => {
let component: HelloWorldComponent;
let fixture: ComponentFixture<HelloWorldComponent>;
let mockHelloWorldService;
beforeEach(async(() => {
mockHelloWorldService = jasmine.createSpyObj(['getHelloWorld']);
TestBed.configureTestingModule({
imports: [],
declarations: [HelloWorldComponent]
});
TestBed.overrideProvider(HelloWorldService, { useValue: mockHelloWorldService });
TestBed.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HelloWorldComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
Here's my component
import { Component, OnInit } from '@angular/core';
import { HelloWorldService } from '../../services/hello-world.service';
@Component({
selector: 'app-hello-world',
templateUrl: './hello-world.component.html',
providers: [HelloWorldService]
})
export class HelloWorldComponent implements OnInit {
helloWorldMessage: any;
constructor(private helloWorldService: HelloWorldService) { }
ngOnInit() {
this.getHelloWorldMsg();
}
getHelloWorldMsg() {
this.helloWorldService
.getHelloWorld()
.subscribe((data) => {
this.helloWorldMessage = data;
}, err => this.handleErrorResponse('There was a problem loading the hello world message', err));
}
handleErrorResponse(errorMsg: string, error?: any) {
console.log("There was a problem getting the message");
}
}
And here's my service
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { environment } from '../../environments/environment';
@Injectable()
export class HelloWorldService {
constructor(private http: HttpClient) { }
getHelloWorld(): Observable<any> {
console.log("Why is the test coming here when I provide a mock?");
var getHelloWorldApiUrl = environment.apiUrl + "/api/v1.0/helloworld/GetHelloWorldMessageAuth";
return this.http
.get(getHelloWorldApiUrl);
}
}
I see two issues in the code above:
HelloWorldService in the providers array, you have specified useClass, but have provided an object (which is what jasmine.createSpyObj() produces), so you should instead specify useValue.I hope this helps.
Ok, you have come quite a long ways! You corrected the two problems I outlined above, have done the overrideProvider() before the compileComponents() as I suggested in a comment to another answer as well as wrapped the beforeEach() inside an async().
The reason I asked to see all the other code was so I could quickly put it up in the following StackBlitz for testing. As you can see in that StackBlitz, the test is now passing.
I only added one line in the beforeEach() to declare a returnValue from your spy, so that the subscribe in your component has an Observable to subscribe to:
mockHelloWorldService.getHelloWorld.and.returnValue(of('Test Message'));
I added this before the call to fixture.detectChanges() since this will invoke ngOnInit(), and the spy needs a return value set BEFORE executing ngOnInit() so that it will execute correctly and not give you the error you were seeing.
I also added a line in the spec to show how you can test that the result of the Observable is correctly set to your component variable:
expect(component.helloWorldMessage).toEqual('Test Message');
Note that you are significantly complicating your testing by specifying the HelloWorldService in the providers array of your component declaration. If you instead provide this service as a singleton in root that will simplify things a lot, including how you test. See details in the Official Docs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With