1

I am getting static null injector error when I am trying to call dialogRef.componentInstance.onAdd method in spec file.

my code is below. 1. Viewer Component

    import { Component, OnInit } from '@angular/core';
    import { MatDialog } from '@angular/material';
    import { ModalPopupComponent } from '../shared/modal-popup/modal-popup.component';
    import { interval } from 'rxjs';
    @Component({
        selector: 'app-viewer',
        templateUrl: './viewer.component.html',
        styleUrls: ['./viewer.component.less']
    })
    export class ViewerComponent implements OnInit {
        userAwareNessText: string;
        secondsCounter = interval(300000);

        constructor(private dialog: MatDialog) { }

        ngOnInit() {
            this.subScribeTimer();
        }
        subScribeTimer(): void {
            this.secondsCounter.subscribe(() => {
                this.askUserAwareness();
            });
        }
        askUserAwareness(): void {
            const dialogRef = this.dialog.open(ModalPopupComponent, {
                width: '250px',
                data: ''
            });

            const sub = dialogRef.componentInstance.onAdd.subscribe((returnData: any) => {
                this.userAwareNessText = returnData;
            });
            dialogRef.afterClosed().subscribe(() => {
                sub.unsubscribe();
            });
        }
    }
  1. Modal PopupComponent

        import { Component, OnInit, ViewChild, EventEmitter } from '@angular/core';
    import { MatDialogRef } from '@angular/material';
    import { CountdownComponent } from 'ngx-countdown';
    
    @Component({
        selector: 'app-modal-popup',
        templateUrl: './modal-popup.component.html',
        styleUrls: ['./modal-popup.component.less']
    })
    export class ModalPopupComponent implements OnInit {
        onAdd = new EventEmitter();
        userAwareNessText: string;
    
        constructor(
            private dialogRef: MatDialogRef<ModalPopupComponent>) { }
    
        @ViewChild('countdown') counter: CountdownComponent;
    
        ngOnInit() {
            this.userAwareNessText = 'User is on the screen!!!';
        }
    
        finishPopUpTimer() {
            this.userAwareNessText = 'User left the screen!!!';
            this.resetTimer();
            this.closePopUp();
            this.toggleParentView();
        }
        resetTimer() {
            this.counter.restart();
        }
        closePopUp() {
            this.dialogRef.close();
            this.onAdd.emit(this.userAwareNessText);
        }
        toggleParentView() {
            this.onAdd.emit(this.userAwareNessText);
        }
    }
    

Here is my spec file of viewer component

    import { async, ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing';
    import { ViewerComponent } from './Viewer.component';
    import { MatDialog, MatDialogRef } from '@angular/material';
    import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';
    import { of } from 'rxjs';
    import { ModalPopupComponent } from '../shared/modal-popup/modal-popup.component';
    import { CountdownComponent } from 'ngx-countdown';


    describe('ViewerComponent', () => {
        let component: ViewerComponent;
        let fixture: ComponentFixture<ViewerComponent>;
        let modalServiceSpy: jasmine.SpyObj<MatDialog>;
        let dialogRefSpyObj = jasmine.createSpyObj(
            {
                afterClosed: of({}),
                close: null,
                componentInstance: {
                    onAdd: (data: any) => of({ data })
                }
            }
        );


        beforeEach(async(() => {
            modalServiceSpy = jasmine.createSpyObj('modalService', ['open']); // , 'componentInstance', 'onAdd'
            TestBed.configureTestingModule({
                declarations: [ViewerComponent, ModalPopupComponent],
                providers: [
                    { provide: MatDialog, useValue: modalServiceSpy },
                ],
                schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
            })
                .compileComponents().then(() => {
                    fixture = TestBed.createComponent(ViewerComponent);
                    component = fixture.componentInstance;
                });
        }));

        beforeEach(() => {
            fixture = TestBed.createComponent(ViewerComponent);
            component = fixture.componentInstance;
            modalServiceSpy.open.and.returnValue(dialogRefSpyObj);
        });

        it('should create component', () => {
            expect(component).toBeTruthy();
        });

        it('should open popup and close popup', fakeAsync(() => {
            let fixture_modalPopup = TestBed.createComponent(ModalPopupComponent);
            let component_modalPopUp = fixture_modalPopup.componentInstance;
            fixture_modalPopup.detectChanges();
            spyOn(component_modalPopUp, 'onAdd');

            component.askUserAwareness();

            expect(modalServiceSpy.open).toHaveBeenCalled();
            expect(component_modalPopUp.onAdd).toHaveBeenCalled();
            expect(dialogRefSpyObj.afterClosed).toHaveBeenCalled();
        }));

    });

I am getting error for below part of code

const sub = dialogRef.componentInstance.onAdd.subscribe((returnData: any) => {
                this.userAwareNessText = returnData;
            });

Please suggest me how can I pass that portion of my code.

Megha shah
  • 232
  • 3
  • 13
  • Can you please the error message you're getting – yurzui Jun 28 '19 at 08:23
  • `onAdd` is defined as a property in `ModalPopupComponent` as `onAdd = new EventEmitter();` but in you spy object it is defined as a function `onAdd: (data: any) => of({ data })` also similar problem exists on `afterClosed`. it is defined as property in your test `afterClosed: of({})` where it is a function on `MatDialogRef`. your whole test code needs to be reworked. – ysf Jun 28 '19 at 08:24
  • @ysf will you please provide me sample code for that? – Megha shah Jul 01 '19 at 07:38
  • if you can provide me a github repo of your setup, i can. – ysf Jul 02 '19 at 08:01

1 Answers1

0

Since you created a spy here:

spyOn(component_modalPopUp, 'onAdd');

Your following stub function that you provided gets overridden

componentInstance: {
    onAdd: (data: any) => of({ data })
}

And you are not returning an Observable from your spy and hence you cannot subscribe to it.

You can just provide a spy in your stub function like :

componentInstance: {
    onAdd: jasmine.createSpy('onAdd')
}

And return values in your test cases depending upon use case,

it('should open popup and close popup', fakeAsync(() => {
    spyOn(component_modalPopUp, 'onAdd');
    dialogRefSpyObj.componentInstance.onAdd.and.returnValue(of({}))
    component.askUserAwareness();
    ...
}));
Akshay Rana
  • 1,455
  • 11
  • 20
  • Hi @Akshay Rana I did what you suggest, then I am getting this 'TypeError: Cannot read property 'and' of undefined' – Megha shah Jun 28 '19 at 09:24
  • Also one more question, I need to provide all dependencies of ModalPopupComponet in Viewer spec file. Is it correct approach? – Megha shah Jun 28 '19 at 09:26
  • Return the value while mocking in that case( onAdd:jasmine.createSpy('onAdd') ), or add the return statement before "modalServiceSpy.open.and.returnValue(dialogRefSpyObj);" – Akshay Rana Jun 28 '19 at 09:32
  • No, you don't need to provide dependencies for Modal component, as these are the unit tests for Viewer component. Mocking open method should be enough. Are there any dependencies that compiler is asking for? – Akshay Rana Jun 28 '19 at 09:35
  • as we are creating spy on componet_ModalPopup, that is component of modal popup component, it is asking for all its dependencies – Megha shah Jun 28 '19 at 09:40
  • On which line? It probably is asking for dependencies because you have added it to your declarations array in configureTestingModule, you don't need it there. – Akshay Rana Jun 28 '19 at 09:56
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/195677/discussion-between-megha-shah-and-akshay-rana). – Megha shah Jun 28 '19 at 09:59