Có cách nào để kiểm tra EventEmitter trong Angular2 không?


87

Tôi có một thành phần sử dụng EventEmitter và EventEmitter được sử dụng khi ai đó trên trang được nhấp vào. Có cách nào để tôi có thể quan sát EventEmitter trong quá trình kiểm tra đơn vị và sử dụng TestComponentBuilder để nhấp vào phần tử kích hoạt phương thức EventEmitter.next () và xem những gì đã được gửi không?


Bạn có thể cung cấp một plunker cho thấy những gì bạn đã thử, sau đó tôi có thể xem xét để bổ sung các phần còn thiếu.
Günter Zöchbauer

Câu trả lời:


205

Bài kiểm tra của bạn có thể là:

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');
});

khi thành phần của bạn là:

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

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

1
Nếu đó là một mỏ neo mà tôi đang nhấp thay vì một nút, liệu bộ chọn truy vấn có phải là một nút thay vì không? Tôi đang sử dụng một cái gì đó chính xác như thành phần đó, nhưng 'kỳ vọng (giá trị) .toBe (' xin chào ');' không bao giờ được chạy. Không biết có phải vì nó là một mỏ neo không.
highkid24

Tôi đã cập nhật câu trả lời của mình bằng một cách kiểm tra rõ ràng hơn, sử dụng gián điệp thay vì trình phát thực và tôi nghĩ nó sẽ hoạt động (đó là những gì tôi thực sự làm cho các mẫu trong ebook của mình).
cexbrayat

Điều này làm việc tuyệt vời cảm ơn! Tôi chưa quen với việc phát triển giao diện người dùng, đặc biệt là đơn vị thử nghiệm nó. Điều này giúp ích rất nhiều. Tôi thậm chí không biết chức năng spyOn tồn tại.
highkid 24

Làm cách nào để kiểm tra điều này nếu sử dụng TestComponent để bọc MyComponent? Ví dụ html = <my-component (myEventEmitter)="function($event)"></my-component>và trong thử nghiệm tôi làm: tcb.overrideTemplate (TestComponent, html) .createAsync (TestComponent)
bekos

1
câu trả lời tuyệt vời - rất súc tích và cho điểm - một mô hình chung rất hữu ích
danday74

48

Bạn có thể sử dụng một gián điệp, tùy thuộc vào phong cách của bạn. Đây là cách bạn có thể dễ dàng sử dụng gián điệp để xem liệu emitcó bị đuổi việc ...

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

Tôi đã cập nhật câu trả lời để không sử dụng async hoặc fakeAsync không cần thiết, điều này có thể gây ra vấn đề như đã chỉ ra trong các nhận xét trước. Câu trả lời này vẫn là một giải pháp tốt kể từ Angular 9.1.7. Nếu có gì thay đổi vui lòng để lại comment mình sẽ cập nhật câu trả lời này. cảm ơn cho tất cả những người đã nhận xét / kiểm duyệt.
Joshua Michael Wagoner

Bạn không nên expectlà gián điệp thực sự (kết quả của spyOn()cuộc gọi)?
Yuri

Tôi đã bỏ lỡ "component.buttonClick ()" sau Spyon. Giải pháp này đã giải quyết vấn đề của tôi. Cảm ơn rất nhiều!
Pearl

2

Bạn có thể đăng ký bộ phát hoặc liên kết với nó, nếu nó là một @Output(), trong mẫu mẹ và kiểm tra thành phần mẹ xem liên kết đã được cập nhật chưa. Bạn cũng có thể gửi một sự kiện nhấp chuột và sau đó đăng ký sẽ kích hoạt.


Vì vậy, nếu tôi đã thích emitter.subscribe (data => {}); làm cách nào để nhận được kết quả () tiếp theo?
highkid24

Chính xác. Hoặc mẫu trong TestComponenthas <my-component (someEmitter)="value=$event">(where someEmitterlà an @Output()) thì thuộc valuetính của TextComponentphải được cập nhật với sự kiện đã gửi.
Günter Zöchbauer

0

Tôi đã có một yêu cầu để kiểm tra độ dài của mảng phát ra. Vì vậy, đây là cách tôi đã làm điều này trên các Câu trả lời khác.

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

-2

Mặc dù các câu trả lời được bình chọn cao nhất hoạt động, nhưng chúng không thể hiện các phương pháp kiểm tra tốt, vì vậy tôi nghĩ tôi sẽ mở rộng câu trả lời của Günter bằng một số ví dụ thực tế.

Hãy tưởng tượng chúng ta có một thành phần đơn giản sau:

@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!');
  }
}

Thành phần là hệ thống đang được thử nghiệm, do thám các phần của nó sẽ phá vỡ tính đóng gói. Kiểm tra thành phần góc chỉ nên biết về ba điều:

  • DOM (được truy cập thông qua ví dụ fixture.nativeElement.querySelector);
  • Tên của @Inputs và @Outputs; và
  • Hợp tác dịch vụ (tiêm qua hệ thống DI).

Bất kỳ điều gì liên quan đến việc gọi trực tiếp các phương thức trên phiên bản hoặc theo dõi các phần của thành phần đều được kết hợp quá chặt chẽ với việc triển khai và sẽ gây thêm khó khăn cho việc tái cấu trúc - chỉ nên sử dụng bộ đôi thử nghiệm cho các cộng tác viên. Trong trường hợp này, vì chúng tôi không có cộng tác viên, nên chúng tôi không cần bất kỳ trò giễu cợt, gián điệp hoặc các bộ đôi thử nghiệm khác.


Một cách để kiểm tra điều này là bằng cách đăng ký trực tiếp với bộ phát, sau đó gọi hành động nhấp chuột (xem Thành phần với đầu vào và đầu ra ):

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!');
  });
});

Mặc dù điều này tương tác trực tiếp với cá thể thành phần, tên của thành @Outputphần là một phần của API công khai, vì vậy nó không được kết hợp quá chặt chẽ.


Ngoài ra, bạn có thể tạo một máy chủ thử nghiệm đơn giản (xem Thành phần bên trong máy chủ thử nghiệm ) và thực sự gắn kết thành phần của bạn:

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

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

sau đó kiểm tra thành phần trong ngữ cảnh:

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!');
  });
});

Đây componentInstancemáy chủ thử nghiệm , vì vậy chúng tôi có thể tự tin rằng chúng tôi không kết hợp quá nhiều với thành phần mà chúng tôi đang thực sự thử nghiệm.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.