I am testing a very simple component that shows/hides the login/out buttons.
For this I am mocking my AuthService
service as that relies AngularFire2.
The problem I am having is that is appears my mock service ( Mock AuthService
) isn't being provided in place of the actual AuthService
.
In the test should show the Facebook login button
, service.isAnonymous
is expected to be undefined. In the actual service, it is. But in the mock service is is true
. This test should fail.
Also, notice I am trying to call the method service.test(false);
; in the mock service this method exists and is public
. But I receive the error:
Property 'test' does not exist on type 'AuthService'.
This suggests that my mock service is not being provided.
You can see how I have tried two way to provide the mock service (one is commented out) in my test spec:
import { DebugElement } from '@angular/core';
import {
async,
inject,
ComponentFixture,
TestBed
} from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { FacebookLoginComponent } from './facebook-login.component';
import { AuthService } from '../shared/auth.service';
import { MockAuthService } from '../shared/testing/auth.service';
describe('FacebookLoginComponent', () => {
let authService: AuthService;
let component: FacebookLoginComponent;
let fixture: ComponentFixture<FacebookLoginComponent>;
let debugElement: DebugElement;
let htmlElement: HTMLElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FacebookLoginComponent ],
// providers: [{ provide: AuthService, useValue: MockAuthService }]
})
.compileComponents();
TestBed.overrideComponent(FacebookLoginComponent, {
set: {
providers: [{ provide: AuthService, useClass: MockAuthService }]
}
})
}));
beforeEach(() => {
fixture = TestBed.createComponent(FacebookLoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
it('should show the Facebook login button', inject([ AuthService ], (service: AuthService) => {
expect(service.isAnonymous).toBeUndefined();
debugElement = fixture.debugElement.query(By.css('button'));
htmlElement = debugElement.nativeElement;
service.test(false);
expect(htmlElement.textContent).toBe('Facebook Login');
}));
it('should show the Logout button', () => {
debugElement = fixture.debugElement.query(By.css('button'));
htmlElement = debugElement.nativeElement;
expect(htmlElement.textContent).toBe('Logout');
});
});
For completeness; here is my mock service, MockAuthService
:
import { Injectable } from '@angular/core';
@Injectable()
export class MockAuthService {
public authState: { isAnonymous: boolean, uid: string };
constructor() {
this.authState = { isAnonymous: true, uid: '0HjUd9owxPZ5kibvUCN6S2DgB4x1' };
}
// public get currentUser(): firebase.User {
// return this.authState ? this.authState : undefined;
// }
// public get currentUserObservable(): Observable<firebase.User> {
// return this.afAuth.authState;
// }
public get currentUid(): string {
return this.authState ? this.authState.uid : undefined;
}
public get isAnonymous(): boolean {
return this.authState ? this.authState.isAnonymous : false;
}
public get isAuthenticated(): boolean {
return !!this.authState;
}
// public logout(): void {
// this.afAuth.auth.signOut();
// }
public test(isAnonymous: boolean) {
this.authState.isAnonymous = isAnonymous;
}
}
I am at a loss as to how to provide the mock instead.
Based on answers and comments so far I have update my mock test spec. However, I am still having the same issue.
I get the error Property 'test' does not exist on type 'AuthService'.
This suggests it is still not substituting the mock for the actual authService
service.
Furthermore, when I add:
public test(test: boolean): boolean {
return test;
}
To the actual service the test fails; but not because of the error above, but because it should — the expectations of the test are not met.
This is my updated spec:
import { DebugElement } from '@angular/core';
import {
async,
inject,
ComponentFixture,
TestBed
} from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { FacebookLoginComponent } from './facebook-login.component';
import { AuthService } from '../shared/auth.service';
import { MockAuthService } from '../shared/testing/auth.service';
describe('FacebookLoginComponent', () => {
let authService: AuthService;
let component: FacebookLoginComponent;
let fixture: ComponentFixture<FacebookLoginComponent>;
let debugElement: DebugElement;
let htmlElement: HTMLElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FacebookLoginComponent ],
providers: [{ provide: AuthService, useClass: MockAuthService }]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FacebookLoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
it('should show the Facebook Login button', inject([ AuthService ], (service: AuthService) => {
debugElement = fixture.debugElement.query(By.css('button'));
htmlElement = debugElement.nativeElement;
expect(htmlElement.textContent).toBe('Facebook Login');
}));
it('should show the Logout button', inject([ AuthService ], (service: AuthService) => {
expect(service.isAnonymous).toBe(true);
debugElement = fixture.debugElement.query(By.css('button'));
htmlElement = debugElement.nativeElement;
service.test(false);
fixture.detectChanges();
expect(htmlElement.textContent).toBe('Logout');
}));
});
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FacebookLoginComponent ],
// providers: [{ provide: AuthService, useValue: MockAuthService }]
})
.compileComponents();
TestBed.overrideComponent(FacebookLoginComponent, {
set: {
providers: [{ provide: AuthService, useClass: MockAuthService }]
}
})
}));
There are two problems. You don't need to override component's providers because you can provide them when configuring the TestBed
.
I assume that you've started overriding the component because the initial configuration didn't work. It didn't work because you've used useValue
instead of useClass
.
// providers: [{ provide: AuthService, useValue: MockAuthService }]
This should do:
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ FacebookLoginComponent ],
providers: [{ provide: AuthService, useClass: MockAuthService }]
}).compileComponents();
}));
EDIT:
When using inject
function, you should use MockAuthService
as a type. TypeScript should stop complaining.
inject([ AuthService ], (service: MockAuthService) => { /* ... */ });
So, I was struggling and losing my mind over this same issue. I had 2 services in my component I needed to mock for testing. One (let's call it MyService
) was successfully using a mock, and the other (let's call MyOtherService
) wouldn't use the mock but the actual service.
Eventually I figured it out (kinda). So this is the class/component I was testing:
@Component({
selector: "app-my-component",
templateUrl: "./my-component.component.html",
styleUrls: ["./my-component.component.scss"],
providers: [MyOtherService]
})
export class MyComponentComponent implements OnInit, OnDestroy {
...private logic...
constructor(
private myService: MyService,
private myOtherService: MyOtherService,
private route: ActivatedRoute,
) {}
..component logic...
See something that MyOtherService
is doing that MyService
isn't?..... It's in the providers in the @Component
!
Turns out I didn't really need that service there when I had it in my constructor, so I removed it and the test started using the mock instead of the actual service.
So, I would check if you're adding services in constructor or in the providers in the @Component
.
Now, I think there must be a way to still mock services that are declared in the providers in the @Component
, but I wasn't able to figure that out and it was easier to remove it since my constructor was getting the service anyway.
I know your question is very old but I hope this helps!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.