Будь-який спосіб протестувати EventEmitter в Angular2?


87

У мене є компонент, який використовує EventEmitter, і EventEmitter використовується, коли натискається хтось на сторінці. Чи є спосіб, яким я можу спостерігати EventEmitter під час модульного тестування та використовувати TestComponentBuilder, щоб клацнути елемент, який запускає метод EventEmitter.next (), і побачити, що було надіслано?


Чи можете ви надати інструмент, який показує, що ви пробували, тоді я можу поглянути, щоб додати відсутні фрагменти.
Günter Zöchbauer

Відповіді:


205

Вашим тестом може бути:

it('should emit on click', () => {
   const fixture = TestBed.createComponent(MyComponent);
   // spy on event emitter
   const component = fixture.componentInstance; 
   spyOn(component.myEventEmitter, 'emit');

   // trigger the click
   const nativeElement = fixture.nativeElement;
   const button = nativeElement.querySelector('button');
   button.dispatchEvent(new Event('click'));

   fixture.detectChanges();

   expect(component.myEventEmitter.emit).toHaveBeenCalledWith('hello');
});

коли ваш компонент:

@Component({ ... })
class MyComponent {
  @Output myEventEmitter = new EventEmitter<string>();

  buttonClick() {
    this.myEventEmitter.emit('hello');
  }
}

1
Якщо це прив'язка, яку я натискаю замість кнопки, чи селектор запиту буде просто замість кнопки? Я використовую щось на зразок цього компонента, але 'очікуємо (значення) .toBe (' привіт ');' ніколи не біжить. Цікаво, чи це тому, що замість цього це якір.
tallkid24

Я оновив свою відповідь більш чистим способом тестування, використовуючи шпигуна замість справжнього випромінювача, і я думаю, що це має спрацювати (це те, що я насправді роблю для зразків у своїй електронній книзі).
cexbrayat

Це чудово працює! Я новачок у розробці інтерфейсу, особливо в модульному тестуванні. Це дуже допомагає. Я навіть не знав, що функція spyOn існує.
tallkid24

Як я можу перевірити це, якщо для обгортання MyComponent використовую TestComponent? Наприклад html = <my-component (myEventEmitter)="function($event)"></my-component>і в тесті я роблю: tcb.overrideTemplate (TestComponent, html) .createAsync (TestComponent)
bekos

1
чудова відповідь - дуже стисла і суть - дуже корисна загальна схема
danday74

48

Ви можете використовувати шпигуна, це залежить від вашого стилю. Ось як ви могли б легко використовувати шпигуна, щоб побачити, чи emitне звільняють ...

it('should emit on click', () => {
    spyOn(component.eventEmitter, 'emit');
    component.buttonClick();
    expect(component.eventEmitter.emit).toHaveBeenCalled();
    expect(component.eventEmitter.emit).toHaveBeenCalledWith('bar');
});

Я оновив відповідь на непотрібне використання async або fakeAsync, що може бути проблематичним, як зазначено в попередніх коментарях. Ця відповідь залишається хорошим рішенням з точки зору Angular 9.1.7. Якщо щось зміниться, залиште коментар, і я оновлю цю відповідь. дякую за всіх, хто коментував / модерував.
Джошуа Майкл Вагонер

Ви не повинні бути expectсправжнім шпигуном (результат spyOn()дзвінка)?
Юрій

Я пропустив "component.buttonClick ()" після Spyon. Це рішення вирішило мою проблему. Дуже дякую!
Перлина

2

Ви можете підписатися на випромінювач або зв’язати його, якщо він є @Output(), у батьківському шаблоні та перевірити в батьківському компоненті, чи оновлення було прив’язане. Ви також можете надіслати клік, і тоді передплата повинна запуститися.


Отже, якщо мені сподобалось emitter.subscribe (дані => {}); як отримати наступний () вихід?
tallkid24

Точно так. Або шаблон у TestComponenthas <my-component (someEmitter)="value=$event">(де someEmitterє @Output()), тоді valueвластивість of TextComponentмає бути оновлено разом із надісланою подією.
Günter Zöchbauer

0

У мене була вимога перевірити довжину випущеного масиву. Ось так я зробив це поверх інших відповідей.

expect(component.myEmitter.emit).toHaveBeenCalledWith([anything(), anything()]);

-2

Хоча відповіді з найбільшою кількістю голосів працюють, вони не демонструють належної практики тестування, тому я подумав, що докладу більше уваги до відповіді Гюнтера декількох практичних прикладах.

Уявімо, що у нас є такий простий компонент:

@Component({
  selector: 'my-demo',
  template: `
    <button (click)="buttonClicked()">Click Me!</button>
  `
})
export class DemoComponent {
  @Output() clicked = new EventEmitter<string>();

  constructor() { }

  buttonClicked(): void {
    this.clicked.emit('clicked!');
  }
}

Компонент - це система, що випробовується, шпигування за частинами розриває капсулювання. Тести кутових компонентів повинні знати лише про три речі:

  • DOM (доступ через, наприклад fixture.nativeElement.querySelector);
  • Назви @Inputs та@Output s; і
  • Послуги, що співпрацюють (вводяться через систему DI).

Все, що передбачає безпосередній виклик методів на екземплярі або шпигунство за частинами компонента, занадто тісно пов’язане з реалізацією, і це додасть тертя рефакторингу - тестові подвійні слід використовувати лише для співавторів. У цьому випадку, оскільки у нас немає співавторів, нам не потрібно мати жодних насмішок, шпигунів чи інших тестових дублів.


Один із способів перевірити це - підписатися безпосередньо на випромінювач, а потім викликати дію клацання (див. Компонент із входами та виходами ):

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ DemoComponent ]
    })
    .compileComponents();
  }));

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

  it('should emit when clicked', () => {
    let emitted: string;
    component.clicked.subscribe((event: string) => {
      emitted = event;
    });

    fixture.nativeElement.querySelector('button').click();

    expect(emitted).toBe('clicked!');
  });
});

Хоча це взаємодіє безпосередньо з екземпляром компонента, ім'я @Outputє частиною загальнодоступного API, тому воно не надто тісно пов'язане.


Крім того, ви можете створити простий тестовий хост (див. Компонент всередині тестового хосту ) і фактично змонтувати свій компонент:

@Component({
  selector: 'test-host',
  template: `
    <my-demo (clicked)="onClicked($event)"></my-demo>
  `
})
class TestHostComponent {
  lastClick = '';

  onClicked(value: string): void {
    this.lastClick = value;
  }
}

потім протестуйте компонент у контексті:

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ TestHostComponent, DemoComponent ]
    })
    .compileComponents();
  }));

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

  it('should emit when clicked', () => {
    fixture.nativeElement.querySelector('button').click();

    expect(component.lastClick).toBe('clicked!');
  });
});

componentInstanceОсь тест господар , так що ми можемо бути впевнені в тому, що ми не дуже пов'язані з компонентом , ми на самому ділі тестування.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.