question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Unit testing dialogs not intuitive

See original GitHub issue

While writing unit tests for a function that handles a angular material 2 dialog using the example code from material 2 i run into problems. I’m a Jasmine newbie but I didn’t had problems to write unit test before. I have to test the result of the afterClose function but i can’t get the handle on dialogRef.

For sure there is a workaround but since this was the first function that was really hard to test and someone liked my post on stackoverflow about it maybe refactoring could make it easier ? http://stackoverflow.com/questions/42852816/unit-testing-angular-material-2-dialogs

            let dialogRef = this.dialog.open(ExtractPageDialog, {
                width: this.EXPORT_DIALOG_WIDTH,
                data: {
                    document: this.document
                }
            });
            dialogRef.afterClosed().subscribe((result: any) => {
                if (result) {
                    let fileId = this.document.fileId;
                    this.docProvider.extractPage(this.document.fileId, result.fromPage, result.toPage).subscribe(() => {
                       () => { //totest },
                       (error) => { //totest }
                    });
                } else {
                    //totest
                }
            });

DOCS: https://material.angular.io/components/component/dialog

Proposal, change the example to be easier to test:

dialogRef.afterClosed().subscribe(this.functionName);

functionName(result: any) {
                if (result) {
                    let fileId = this.document.fileId;
                    this.docProvider.extractPage(this.document.fileId, result.fromPage, result.toPage).subscribe(() => {
                       () => { //totest },
                       (error) => { //totest }
                    });
                } else {
                    //totest
                }
            }
}

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:10 (1 by maintainers)

github_iconTop GitHub Comments

23reactions
lordgregcommented, Jan 12, 2018

Even though this issue has been closed I must say I have never seen such a complex and non-understandable unit tests definition with so many generic variables. overlayContainerElement, viewContainerFixture, ComponentWithChildViewContainer etc…

You have to know each class and its definition perfectly to understand the code. Who would need comments anyways, right?

8reactions
fizxmikecommented, Sep 11, 2018

Angular 6.1.1 Material 6.4.3

40+ lines of boiler plate. I learn more about Angular when writing tests than anything else!

Very simple “save” and “cancel” test for EditDialogComponent:

@Directive({
  // tslint:disable-next-line:directive-selector
  selector: 'view-container-directive'
})
class ViewContainerDirective {
  constructor(public viewContainerRef: ViewContainerRef) { }
}

@Component({
  selector: 'app-view-container-component',
  template: `<view-container-directive></view-container-directive>`,
})
class ViewContainerComponent {
  @ViewChild(ViewContainerDirective) childWithViewContainer: ViewContainerDirective;

  get childViewContainer() {
    return this.childWithViewContainer.viewContainerRef;
  }
}

describe('EditObjectiveDialog', () => {
  let dialog: MatDialog;
  let overlayContainerElement: HTMLElement;

  let testViewContainerRef: ViewContainerRef;
  let viewContainerFixture: ComponentFixture<ViewContainerComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        ViewContainerComponent,
        ViewContainerDirective,
        EditDialogComponent,
      ],
      imports: [
        MaterialModule // module of material modules I use
      ],
      providers: [
        { provide: OverlayContainer, useFactory: () => {
          overlayContainerElement = document.createElement('div');
          return { getContainerElement: () => overlayContainerElement };
        }}
      ],
    });

    TestBed.overrideModule(BrowserDynamicTestingModule, {
     // not sure why I needed this, but error message told me to include it
     set: {
        entryComponents: [ EditDialogComponent ]
      }
    });

    TestBed.compileComponents();
  }));

  beforeEach(() => {
    viewContainerFixture = TestBed.createComponent(ViewContainerComponent);
    viewContainerFixture.detectChanges();
    testViewContainerRef = viewContainerFixture.componentInstance.childViewContainer;
  });

  beforeEach(inject([MatDialog], (d: MatDialog) => {
    dialog = d;
  }));

  describe('Save and Cancel', () => {
    let testStructureObjective: StructureObjective;
    let dialogRef: MatDialogRef<EditDialogComponent, any>;
    let afterCloseCallback: jasmine.Spy;

    beforeEach(() => {
      dialogRef = dialog.open(EditDialogComponent, {
        viewContainerRef: testViewContainerRef,
        data: {
            // DialogData goes here
      }});

      afterCloseCallback = jasmine.createSpy('afterClose callback');
      dialogRef.afterClosed().subscribe(afterCloseCallback);
    });

    it('should return input on save if no edits', async(() => {
      // no edits
      // click save
      const saveButton: DebugElement = viewContainerFixture.debugElement.query(By.css('#saveButton'));
      saveButton.triggerEventHandler('click', null);

      viewContainerFixture.detectChanges();

      viewContainerFixture.whenStable().then(() => {
        expect(afterCloseCallback).toHaveBeenCalledWith({...});
        expect(dialogRef.componentInstance).toBeFalsy(); // is closed
      });
    }));

    it('should return undefined if cancelled', async(() => {
      // no edits
      // click cancel
      const cancelButton: DebugElement = viewContainerFixture.debugElement.query(By.css('#cancelButton'));
      cancelButton.triggerEventHandler('click', null);

      viewContainerFixture.detectChanges();

      viewContainerFixture.whenStable().then(() => {
        expect(afterCloseCallback).toHaveBeenCalledWith(undefined);
        expect(dialogRef.componentInstance).toBeFalsy(); // is closed
      });
    }));

  });
Read more comments on GitHub >

github_iconTop Results From Across the Web

Yes, User Prompts and Unit Tests Can Co-Exist
Nevertheless, it's not true that the presence of user prompts is an absolute bar to unit testing. Depending on your implementation, unit testing ......
Read more >
Why practicing DRY in tests is bad for you - DEV Community ‍ ‍
I'm going to share my point of view on practicing DRY in unit tests and why I think it is bad for you....
Read more >
Testing Runtime Permissions: Lessons Learned
Permission model was not very user-friendly before Marshmallow: every time ... First, let's write a test that will simply verify that the request...
Read more >
Painless Unit Testing with Kotlintest & Mockk | Zuhaib Ahmad
In this post, let's understand why unit tests serve as the backbone of successful products and learn a new way of writing tests...
Read more >
Build and Test Modal using React features and DOM events ...
This series IS NOT about setting up testing environments for React — The goal is to help you build intuition on what to...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found