'Angular with Jest Unit Test: Cannot read properties of undefined (reading 'pipe')
I want to unit test an Angular component with a NgRx store.
This is my component:
import { Component, OnInit } from '@angular/core';
import { FormGroup, Validators } from '@angular/forms';
import { select } from '@ngrx/store';
import { catchError, Observable, of } from 'rxjs';
import { View } from 'src/app/core/models/view.model';
import { selectIsAuthenticated } from 'src/app/core/store/selectors/auth.selectors';
import { BaseAuthComponent } from '../base-auth/base-auth.component';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'],
})
export class LoginComponent extends BaseAuthComponent implements OnInit {
isAuthenticated$: Observable<boolean>;
loginForm: FormGroup;
formSubmitted = false;
loginError = false;
ngOnInit(): void {
this.isAuthenticated$ = this.store.pipe(select(selectIsAuthenticated));
this.loginForm = this.formBuilder.group({
email: ['[email protected]', [Validators.required, Validators.email]],
password: ['wfojewfioerjferergER$§T', [Validators.required]],
rememberMe: [false],
});
}
login(): void {
this.formSubmitted = true;
if(this.loginForm.valid) {
this.authService.login(this.loginForm.value).pipe(
catchError(() => {
this.formSubmitted = false;
this.loginError = true;
return of(false);
})
)
.subscribe(data => {
data ? this.onSuccess.emit() : {};
});
}
}
changeView(): void {
this.onViewChange.emit(View.REGISTER);
}
}
My test file looks like this:
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideMockStore } from '@ngrx/store/testing';
import { selectIsAuthenticated } from 'src/app/core/store/selectors/auth.selectors';
import { LoginComponent } from './login.component';
const spyOn = jest.spyOn;
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
const initialState = {
auth: {
authenticated: false
}
};
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [],
declarations: [ LoginComponent ],
providers: [
provideMockStore({
initialState,
selectors: [
{ selector: selectIsAuthenticated, value: true }
]
})
]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
The login component extends from this component:
import { Component, EventEmitter, Output } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { Store } from '@ngrx/store';
import { View } from 'src/app/core/models/view.model';
import { AuthService } from 'src/app/core/services/auth/auth.service';
import { FormService } from 'src/app/core/services/form.service';
import { AppState } from 'src/app/core/store/reducers';
@Component({
template: ''
})
export abstract class BaseAuthComponent {
@Output() onSuccess = new EventEmitter();
@Output() onViewChange = new EventEmitter<View>();
constructor(
protected formBuilder: FormBuilder,
protected authService: AuthService,
protected store: Store<AppState>,
public formService: FormService
) { }
}
If I try to run this test I get the following error:
FAIL src/app/modules/auth/login/login.component.spec.ts
LoginComponent
× should create (187 ms)
● LoginComponent › should create
TypeError: Cannot read properties of undefined (reading 'pipe')
20 |
21 | ngOnInit(): void {
> 22 | this.isAuthenticated$ = this.store.pipe(select(selectIsAuthenticated));
| ^
23 |
24 | this.loginForm = this.formBuilder.group({
25 | email: ['[email protected]', [Validators.required, Validators.email]],
at LoginComponent.ngOnInit (src/app/modules/auth/login/login.component.ts:22:40)
at callHook (node_modules/@angular/core/fesm2015/core.mjs:2526:22)
at callHooks (node_modules/@angular/core/fesm2015/core.mjs:2495:17)
at executeInitAndCheckHooks (node_modules/@angular/core/fesm2015/core.mjs:2446:9)
at refreshView (node_modules/@angular/core/fesm2015/core.mjs:9479:21)
at renderComponentOrTemplate (node_modules/@angular/core/fesm2015/core.mjs:9578:9)
at tickRootContext (node_modules/@angular/core/fesm2015/core.mjs:10809:9)
at detectChangesInRootView (node_modules/@angular/core/fesm2015/core.mjs:10834:5)
at RootViewRef.detectChanges (node_modules/@angular/core/fesm2015/core.mjs:21757:9)
at ComponentFixture._tick (node_modules/@angular/core/fesm2015/testing.mjs:141:32)
at node_modules/@angular/core/fesm2015/testing.mjs:154:22
at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:407:30)
at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:3765:43)
at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:406:56)
at Object.onInvoke (node_modules/@angular/core/fesm2015/core.mjs:25826:33)
at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:406:56)
at Zone.Object.<anonymous>.Zone.run (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:167:47)
at NgZone.run (node_modules/@angular/core/fesm2015/core.mjs:25680:28)
at ComponentFixture.detectChanges (node_modules/@angular/core/fesm2015/testing.mjs:153:25)
at src/app/modules/auth/login/login.component.spec.ts:36:13
at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:407:30)
at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:3765:43)
at ZoneDelegate.Object.<anonymous>.ZoneDelegate.invoke (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:406:56)
at Zone.Object.<anonymous>.Zone.run (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:167:47)
at Object.wrappedFunc (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:4250:34)
I already tried to import StoreModule.forRoot(reducers), but if I rerun the test I get another error:
Unexpected value 'StoreRootModule' imported by the module 'DynamicTestModule'. Please add an @NgModule annotation.
I tried:
providers: [
{
provide: Store,
useValue: {
pipe: of({})
}
}
]
and
providers: [
{
provide: Store,
useClass: MockStore
}
]
I also followed the guide on the NgRx website on how to mock a store NgRx Testing. Neither worked.
My Angular project is running version 13.0.0 and Jest is running version 27.4.3.
If someone could help me with this, I would really appreciate that.
Solution 1:[1]
I copy pasted your code and adapted the lines I didn't have access to but my tests failed with another error
NullInjectorError: StaticInjectorError(DynamicTestModule)[LoginComponent -> FormBuilder]:
StaticInjectorError(Platform: core)[LoginComponent -> FormBuilder]:
NullInjectorError: No provider for FormBuilder!
Adding ReactiveFormsModule to the import section of the testfile solved this and the test passed
imports: [ReactiveFormsModule],
I don't think it's supposed to make a difference but you could also try this shorter syntax:
this.isAuthenticated$ = this.store.select(selectIsAuthenticated);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
