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