简体   繁体   中英

Angular Jasmine testing Eventemitter subscription

I am trying to test the following component in Angular, specifically the eventEmitter that is residing on the userService.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { RegisterUser } from 'src/app/classes/register-user';
import { ToastController } from '@ionic/angular';
import { UserService } from 'src/app/services/user.service';
import { Result } from 'src/app/classes/result';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { User } from 'src/app/classes/user';

/**
 * Register-user page, used for creating new users for the system
 */
@Component({
  selector: 'app-register-user',
  templateUrl: './register-user.page.html',
  styleUrls: ['./register-user.page.scss'],
})
export class RegisterUserPage implements OnDestroy {
  constructor(private toastController: ToastController, private userService: 
  UserService) { }

  /**
   * Unsubscribes from Observables when component gets destroyed
   */
  ngOnDestroy(): void {
    this.userService.resultEvent.unsubscribe();
    console.log('unsub');
  }

  /**
   * Function creates a new user and subscribes to resultEvent
   * @param user The new user
   */
  public registerUser(user: RegisterUser) {
    this.userService.registerUser(user);
    this.userService.resultEvent.subscribe((result: Result) => this.showToast(result));
    console.log('subscribed', this.userService.resultEvent);
  }

/**
   * Result handler, which will show result toast
   * @param result Result from the service
   */
  private async showToast(result: Result) {
    console.log('called')
    if (result.success) {
      this.toastController.create({
        message: 'Benutzer angelegt',
        duration: 2000,
      }).then((toast) => {
        toast.present();
      });
    } else {
      this.toastController.create({
        message: 'Ein Fehler ist aufgetreten Code:(' + result.httpCode + ')',
        duration: 2000,
      }).then((toast) => {
        toast.present();
      });
    }
  }
}

My test is the following: I am running the registerUser() method, which subscribes to the eventEmitter in the userService (mocked in test), which then calls the showToast() method, which will display a toast with the result that gets returned by the service in the Event as value.

import { CUSTOM_ELEMENTS_SCHEMA, EventEmitter } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { RegisterUserPage } from './register-user.page';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { UserService } from 'src/app/services/user.service';
import { RegisterUser } from 'src/app/classes/register-user';
import { ToastController } from '@ionic/angular';
import { Result } from 'src/app/classes/result';

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

  const userServiceSpy = {
    registerUser() {},
    async getAllUsersAsync() {},
    resultEvent: new EventEmitter()
  };

  const toastControllerMock = {
    create({}) {return new Promise((resolve, reject) => {
      resolve();
    });
    }
  };

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ RegisterUserPage ],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
      imports: [FormsModule, ReactiveFormsModule],
      providers: [
         { provide: UserService, useValue: userServiceSpy },
         { provide: ToastController, useValue: toastControllerMock }
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(RegisterUserPage);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should display the result', async () => {
    const userService = TestBed.get(UserService);
    const toastController = TestBed.get(ToastController);
    spyOn(toastController, 'create').and.callThrough();

    component.registerUser(new RegisterUser('', '', '', '', ''));
    userService.resultEvent.emit(new Result(400, true));

    const successMessage = {
      message: 'Benutzer angelegt',
      duration: 2000,
    };

    const failMessage = {
      message: 'Ein Fehler ist aufgetreten Code:(500)',
      duration: 2000,
    };

    component['showToast'](new Result(200, true));
    expect(toastController.create).toHaveBeenCalledWith(successMessage);
    component['showToast'](new Result(500, false));
    expect(toastController.create).toHaveBeenCalledWith(failMessage);
  });
});

Somehow the ngDestroy() function gets called, which unsubs from the eventEmitter before the test finishes
Therefor I am experiencing a objectUnsubscribed error.

Any idea how to fix this?

Fixed it the following way: Instead of unsubscribing from the eventEmitter, I have saved the subscription into a variable of type Subscription and unsubscribed from that.

export class ...{

  public resultSubsciption: Subscription;

  ngOnDestroy(): void {
    this.resultSubsciption.unsubscribe();
  }

  public registerUser(user: RegisterUser) {
    this.userService.registerUser(user);
    this.resultSubsciption = this.userService.resultEvent.subscribe((result: Result) => 
    this.showToast(result));
  }
}

Subscription management should be done this way:
ObjectUnsubscribedError: object unsubscribed error when I am using ngx-progress in angular 2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM