I'm finding it hard to debug why my ionic page is complaining that the AuthService does not have a Storage Provider. I tried creating a new AuthService in the TestBed but no dice. Could you also explain why this is an issue so I can learn from it? I can't find anything about Injecting a service which is injecting the storage module.
Failed: StaticInjectorError(DynamicTestModule)[AuthService -> Storage]: StaticInjectorError(Platform: core)[AuthService -> Storage]: NullInjectorError: No provider for Storage!
import {
async,
ComponentFixture,
TestBed,
fakeAsync,
tick,
} from '@angular/core/testing';
import { IonicModule } from '@ionic/angular';
import { RouterTestingModule } from '@angular/router/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import {
HttpClientTestingModule,
HttpTestingController,
} from '@angular/common/http/testing';
import { SignInPage } from './sign-in.page';
import { User } from 'src/app/classes/user';
import { AuthService } from '../../services/auth-service/auth.service';
import { UsersDataPublic } from '../../services/users-service/users-data-public';
import { UserProfileService } from '../../services/user-profile/user-profile.service';
import { UserProfileDatabase } from '../../services/user-profile/user-profile-db.service';
import { AppConstants } from '../../constants/app.constants';
import { Storage } from '@ionic/Storage';
import { NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA, inject } from '@angular/core';
const storageIonicMock: any = {
get: () => new Promise<any>((resolve, reject) => resolve('As2342fAfgsdr')),
set: () => new Promise<any>((resolve, reject) => resolve('As2342fAfgsdr'))
};
describe('SignInPage', () => {
let component: SignInPage;
let httpTestingController: HttpTestingController;
let fixture: ComponentFixture<SignInPage>;
let httpClient: HttpClient;
let service: UsersDataPublic;
let userProfileservice: UserProfileService;
let authService: AuthService;
let auth;
let userProfileDatabase: UserProfileDatabase;
let constants: AppConstants;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SignInPage],
providers: [
UsersDataPublic,
UserProfileService,
UserProfileDatabase,
AppConstants,
AuthService,
{
provide: Storage,
useValue: storageIonicMock
}
],
imports: [
IonicModule.forRoot(),
ReactiveFormsModule,
FormsModule,
RouterTestingModule,
FormsModule,
HttpClientTestingModule,
],
schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents();
fixture = TestBed.createComponent(SignInPage);
constants = TestBed.get(AppConstants);
service = TestBed.get(UsersDataPublic);
userProfileservice = TestBed.get(UserProfileService);
authService = TestBed.get(new AuthService(storageIonicMock, httpClient, constants));
userProfileDatabase = TestBed.get(UserProfileDatabase);
httpClient = TestBed.get(HttpClient);
httpTestingController = TestBed.get(HttpTestingController);
component = fixture.componentInstance;
fixture.detectChanges();
}));
it('should create', () => {
expect(component).toBeTruthy();
});
it('form invalid when empty', () => {
expect(component.signinForm.valid).toBeFalsy();
});
it('should run ngOnInit 1 time', fakeAsync(() => {
fixture.detectChanges();
spyOn(component, 'ngOnInit').and.callThrough();
component.ngOnInit();
tick(); // simulates the passage of time until all pending asynchronous activities finish
fixture.detectChanges();
expect(component.ngOnInit).toHaveBeenCalledTimes(1);
}));
it('should have a title of Sign In', () => {
const title: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
'#home-title'
);
expect(title.textContent).toEqual('Sign In');
});
it('should run signIn 1 time', fakeAsync(() => {
fixture.detectChanges();
spyOn(component, 'signIn').and.callThrough();
component.signIn();
tick(); // simulates the passage of time until all pending asynchronous activities finish
fixture.detectChanges();
expect(component.signIn).toHaveBeenCalledTimes(1);
}));
it('should set the user object to the values set by signIn', fakeAsync(() => {
const user = {
ppsn: '13322277P',
day: '1',
month: '1',
year: '1970',
password: 'Testing123!',
};
let ppsn: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
'#ppsn'
);
let day: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
'#day'
);
let month: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
'#month'
);
let year: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
'#year'
);
let password: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
'#password'
);
ppsn.value = '13322277P';
day.value = '1';
month.value = '1';
year.value = '1970';
password.value = 'Testing123!';
fixture.detectChanges();
spyOn(component, 'signIn').and.callThrough();
component.signIn();
tick(); // simulates the passage of time until all pending asynchronous activities finish
fixture.detectChanges();
expect(component.signIn).toHaveBeenCalledTimes(1);
expect(component.userDetails).toEqual(user);
}));
it('should not match the user object to the values set by signIn', fakeAsync(() => {
const user = {
ppsn: '13322277P',
day: '1',
month: '2',
year: '1970',
password: 'Testing123!',
};
let ppsn: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
'#ppsn'
);
let day: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
'#day'
);
let month: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
'#month'
);
let year: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
'#year'
);
let password: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
'#password'
);
ppsn.value = '13322277P';
day.value = '1';
month.value = '1';
year.value = '1970';
password.value = 'Testing123!';
fixture.detectChanges();
spyOn(component, 'signIn').and.callThrough();
component.signIn();
tick(); // simulates the passage of time until all pending asynchronous activities finish
fixture.detectChanges();
expect(component.signIn).toHaveBeenCalledTimes(1);
expect(component.userDetails).not.toEqual(user);
}));
});
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { catchError } from 'rxjs/operators';
import { throwError, Observable, BehaviorSubject } from 'rxjs';
import { AppConstants } from '../../constants/app.constants';
import { Storage } from '@ionic/storage';
@Injectable({
providedIn: 'root'
})
export class AuthService {
authenticationState = new BehaviorSubject(false);
authDetails = {authenticated: false, type: null, myAccountPortalAuthenticated: false };
constructor(private storage: Storage, private httpClient: HttpClient, private constants: AppConstants) { }
handleError(error: HttpErrorResponse) {
let errorMessage = 'Unknown error!';
if (error.error instanceof ErrorEvent) {
errorMessage = `Error: ${error.error.message}`;
} else {
errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
}
return throwError(errorMessage);
}
login(url): Observable<any> {
return this.httpClient.post(url, {})
.pipe(
catchError((err: HttpErrorResponse) => {
return throwError(err);
})
);
}
devLogin(url): Observable<any> {
return this.httpClient.get(this.constants.LOCAL_REST_API_SERVER + '/users')
.pipe(
catchError((err: HttpErrorResponse) => {
return throwError(err);
})
);
}
logout(url): Observable<any> {
return this.httpClient.post(url, {})
.pipe(
catchError((err: HttpErrorResponse) => {
return throwError(err);
})
);
}
getAuthenticationDetails() {
return this.storage.get('auth');
}
getAuthenticationState(): Observable<boolean> {
return this.authenticationState.asObservable();
}
setIsAuthenticated(value, loginType) {
this.authDetails.authenticated = value;
this.authDetails.type = loginType;
this.storage.set('auth', this.authDetails);
this.authenticationState.next(value);
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { UserProfileDatabase } from '../../services/user-profile/user-profile-db.service';
import { UserProfileService } from '../../services/user-profile/user-profile.service';
import { AuthService } from '../../services/auth-service/auth.service';
import { SignInPageRoutingModule } from './sign-in-routing.module';
import { SignInPage } from './sign-in.page';
import { ReactiveFormsModule } from '@angular/forms';
import { Storage, IonicStorageModule } from '@ionic/Storage';
@NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
SignInPageRoutingModule,
ReactiveFormsModule,
IonicStorageModule.forRoot()
],
declarations: [SignInPage],
providers: [UserProfileDatabase, UserProfileService, AuthService]
})
export class SignInPageModule {}
Change your script to :-
import {
async,
ComponentFixture,
TestBed,
fakeAsync,
tick,
} from "@angular/core/testing";
import { IonicModule } from "@ionic/angular";
import { RouterTestingModule } from "@angular/router/testing";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { HttpClient } from "@angular/common/http";
import {
HttpClientTestingModule,
HttpTestingController,
} from "@angular/common/http/testing";
import { SignInPage } from "./sign-in.page";
import { User } from "src/app/classes/user";
import { AuthService } from "../../services/auth-service/auth.service";
import { UsersDataPublic } from "../../services/users-service/users-data-public";
import { UserProfileService } from "../../services/user-profile/user-profile.service";
import { UserProfileDatabase } from "../../services/user-profile/user-profile-db.service";
import { AppConstants } from "../../constants/app.constants";
import {
NO_ERRORS_SCHEMA,
CUSTOM_ELEMENTS_SCHEMA,
inject,
} from "@angular/core";
class MockAuthService {
constructor() {}
handleError(error) {}
login(url) {}
devLogin(url) {}
logout(url) {}
getAuthenticationDetails() {}
getAuthenticationState() {}
setIsAuthenticated() {}
}
describe("SignInPage", () => {
let component: SignInPage;
let httpTestingController: HttpTestingController;
let fixture: ComponentFixture<SignInPage>;
let httpClient: HttpClient;
let service: UsersDataPublic;
let userProfileservice: UserProfileService;
let authService: AuthService;
let auth;
let userProfileDatabase: UserProfileDatabase;
let constants: AppConstants;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SignInPage],
providers: [
UsersDataPublic,
UserProfileService,
UserProfileDatabase,
AppConstants,
{ provide: AuthService, useClass: MockAuthService },
],
imports: [
IonicModule.forRoot(),
ReactiveFormsModule,
FormsModule,
RouterTestingModule,
FormsModule,
HttpClientTestingModule,
],
schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents();
fixture = TestBed.createComponent(SignInPage);
constants = TestBed.get(AppConstants);
service = TestBed.get(UsersDataPublic);
userProfileservice = TestBed.get(UserProfileService);
userProfileDatabase = TestBed.get(UserProfileDatabase);
httpClient = TestBed.get(HttpClient);
httpTestingController = TestBed.get(HttpTestingController);
authService = TestBed.get(AuthService);
component = fixture.componentInstance;
fixture.detectChanges();
}));
it("should create", () => {
expect(component).toBeTruthy();
});
it("form invalid when empty", () => {
expect(component.signinForm.valid).toBeFalsy();
});
it("should run ngOnInit 1 time", fakeAsync(() => {
fixture.detectChanges();
spyOn(component, "ngOnInit").and.callThrough();
component.ngOnInit();
tick(); // simulates the passage of time until all pending asynchronous activities finish
fixture.detectChanges();
expect(component.ngOnInit).toHaveBeenCalledTimes(1);
}));
it("should have a title of Sign In", () => {
const title: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
"#home-title"
);
expect(title.textContent).toEqual("Sign In");
});
it("should run signIn 1 time", fakeAsync(() => {
fixture.detectChanges();
spyOn(component, "signIn").and.callThrough();
component.signIn();
tick(); // simulates the passage of time until all pending asynchronous activities finish
fixture.detectChanges();
expect(component.signIn).toHaveBeenCalledTimes(1);
}));
it("should set the user object to the values set by signIn", fakeAsync(() => {
const user = {
ppsn: "13322277P",
day: "1",
month: "1",
year: "1970",
password: "Testing123!",
};
let ppsn: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
"#ppsn"
);
let day: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
"#day"
);
let month: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
"#month"
);
let year: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
"#year"
);
let password: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
"#password"
);
ppsn.value = "13322277P";
day.value = "1";
month.value = "1";
year.value = "1970";
password.value = "Testing123!";
fixture.detectChanges();
spyOn(component, "signIn").and.callThrough();
component.signIn();
tick(); // simulates the passage of time until all pending asynchronous activities finish
fixture.detectChanges();
expect(component.signIn).toHaveBeenCalledTimes(1);
expect(component.userDetails).toEqual(user);
}));
it("should not match the user object to the values set by signIn", fakeAsync(() => {
const user = {
ppsn: "13322277P",
day: "1",
month: "2",
year: "1970",
password: "Testing123!",
};
let ppsn: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
"#ppsn"
);
let day: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
"#day"
);
let month: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
"#month"
);
let year: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
"#year"
);
let password: HTMLInputElement = fixture.debugElement.nativeElement.querySelector(
"#password"
);
ppsn.value = "13322277P";
day.value = "1";
month.value = "1";
year.value = "1970";
password.value = "Testing123!";
fixture.detectChanges();
spyOn(component, "signIn").and.callThrough();
component.signIn();
tick(); // simulates the passage of time until all pending asynchronous activities finish
fixture.detectChanges();
expect(component.signIn).toHaveBeenCalledTimes(1);
expect(component.userDetails).not.toEqual(user);
}));
});