Why am I getting a “Failed: Cannot read property 'subscribe' of undefined” while running tests?

谁说胖子不能爱 提交于 2021-01-28 11:17:51

问题


I am relatively new to testing in Angular2+ and am setting up my files for testing. Since building the project, I've written a lot of code and am now trying to test parts of it. However, lots of those tests are now broken so I am now trying to delete the unneeded default tests or fix the ones that are useful.

This particular test won't run because it fails before it hits the It() testing method. I'm not sure what I'm missing, any help would be appreciated.

I am receiving this error:

Failed: Cannot read property 'subscribe' of undefined
TypeError: Cannot read property 'subscribe' of undefined
    at AppComponent../src/app/app.component.ts.AppComponent.setupLanguageTranslation (src/app/app.component.ts:48:26)
    at AppComponent../src/app/app.component.ts.AppComponent.ngOnInit (src/app/app.component.ts:32:14)
    at checkAndUpdateDirectiveInline (node_modules/@angular/core/fesm5/core.js:22089:1)
    at checkAndUpdateNodeInline (node_modules/@angular/core/fesm5/core.js:23353:1)
    at checkAndUpdateNode (node_modules/@angular/core/fesm5/core.js:23315:1)
    at debugCheckAndUpdateNode (node_modules/@angular/core/fesm5/core.js:23949:1)
    at debugCheckDirectivesFn (node_modules/@angular/core/fesm5/core.js:23909:1)
    at Object.eval [as updateDirectives] (ng:///DynamicTestModule/AppComponent_Host.ngfactory.js:9:9)
    at Object.debugUpdateDirectives [as updateDirectives] (node_modules/@angular/core/fesm5/core.js:23901:1)
    at checkAndUpdateView (node_modules/@angular/core/fesm5/core.js:23297:1)

I've tried a variety of articles with similar issues including, but not limited to:

  • Cannot read property 'subscribe' of undefined after running npm test (Angular 2 unit testing)
  • https://www.reddit.com/r/Angular2/comments/87w8su/what_am_im_doing_wrong_with_observables_and/
  • karma TypeError "Cannot read property 'subscribe' of undefined"

App.component.ts

export class AppComponent implements OnInit, OnDestroy {
    title = 'planox';
    showError = false;
    errorMessage = '';
    translatedText: string;

    constructor(private _translate: TranslateService,
                private utils: Utils) {}

    ngOnInit(): void {
        this.setupLanguageTranslation();
    }

    ngOnDestroy(): void {
        this._translate.onLangChanged.unsubscribe();
    }

    setupLanguageTranslation() {
        this.subscribeToLangChanged();
        // set language
        this._translate.setDefaultLang('en'); // set English as default
        this._translate.enableFallback(true); // enable fallback

        // set current language
        this.selectLang('en');

        this.utils.error.subscribe(response => {
            this.errorMessage = response;
            this.showError = true;
            setTimeout( () => {
                this.showError = false;
            }, 2000);
        });
    }

    selectLang(lang: string) {
        // set default;
        this._translate.use(lang);
    }

    subscribeToLangChanged() {
        // refresh text
        this.refreshText();
        return this._translate.onLangChanged.value;
    }

    refreshText() {
        // refresh translation when language change
        this.translatedText = this._translate.instant('hello world');
    }

}

app.component.spec.ts

import {TestBed, async, ComponentFixture, inject} from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
import {TranslateService} from './translate/translate.service';
import Utils from './shared/utils';
import {MockTranslateService} from './mocks/mockTranslateService';
import {MockUtils} from './mocks/mockUtils';
import {TRANSLATIONS} from './translate/translation';


describe('AppComponent', () => {
    let fixture: ComponentFixture<AppComponent>;

    beforeEach(async(() => {
        const mockTranslate = new MockTranslateService(TRANSLATIONS);
        TestBed.configureTestingModule({
            imports: [
                RouterTestingModule
            ],
            declarations: [
                AppComponent
            ],
            providers: [
                {provide: TranslateService, useValue: mockTranslate},
                {provide: Utils, useValue: MockUtils},
            ]
        });

        fixture = TestBed.createComponent(AppComponent);
        fixture.detectChanges();
    }));

    it('should create the app', () => {
        fixture = TestBed.createComponent(AppComponent);
        const app = fixture.debugElement.componentInstance;
        expect(app).toBeTruthy();
    });
});

Utils.ts:

import {Subject, throwError} from 'rxjs';
import {Injectable} from '@angular/core';
import {Router} from '@angular/router';
import {Role} from '../classes/role/role.enum';

export enum KEY_CODE {
    ENTER = 13,
    RIGHT_ARROW = 39,
    LEFT_ARROW = 37
}

@Injectable({
    providedIn: 'root'
})
export default class Utils {
    error: Subject<string> = new Subject<string>();

    constructor(private router: Router) {}

    handleError(service: string, error: Response | any, customMessage?: string) {
        const err = service + '::handleError' + '=  ' + error;
        this.error.next(customMessage);
        return throwError(error);
    }

    encodeBase64(value: any) {
        return btoa(JSON.stringify(value));
    }

    decodeBase64(value: any) {
        return atob(value);
    }

    navigateToDefaultPage(usersRole: Role) {
        if (usersRole === Role.Employee) {
            this.router.navigate(['/dashboardNavigation', {outlets: {dashNav: ['productionDashboard']}}]);
        } else if (usersRole === Role.Client) {
            this.router.navigate(['/clientDashboard']);
        } else if (usersRole === Role.OfficeAdmin) {
            this.router.navigate(['/dashboardNavigation', {outlets: {dashNav: ['enterpriseDashboard']}}]);
        } else {
            this.router.navigate(['/auth']);
        }
    }

    validateEmail(inputText: string) {
        const mailFormat = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
        if (inputText.match(mailFormat)) {
            return true;
        } else {
            return false;
        }
    }
}

mockUtils.ts

import Utils from "../shared/utils";
import {Subject} from "rxjs";

export class MockUtils extends Utils {
    error = new Subject<string>();
}

Basically I need help setting up the structure of the testing page so that I can start writing useful tests.

I can provide any additional code you may need to help, wasn't sure what all would be needed for this.


回答1:


delete fixture.detectChanges() from the beforeEach method, remove it from there, and moving it into each test if needed will fix it for you, i think it's a bug, seen it on Angular 6 and Angular 7

so your beforeEach will change to:

beforeEach(async(() => {
    const mockTranslate = new MockTranslateService(TRANSLATIONS);
    TestBed.configureTestingModule({
        imports: [
            RouterTestingModule
        ],
        declarations: [
            AppComponent
        ],
        providers: [
            {provide: TranslateService, useValue: mockTranslate},
            {provide: Utils, useValue: MockUtils},
        ]
    });

    fixture = TestBed.createComponent(AppComponent);
    // detection deleted here
}));


来源:https://stackoverflow.com/questions/55958449/why-am-i-getting-a-failed-cannot-read-property-subscribe-of-undefined-while

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!