11
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

HubbleAdvent Calendar 2023

Day 11

@Outputイベントをモックし、テストする

Last updated at Posted at 2023-12-10

はじめに

この記事はHubble Advent Calendar 2023の7日目1の記事です。
Hubbleのフロントエンドチームではユニットテストに重きを置いていて、ほとんどのテストをユニットテストで書いています。
また、Presentationalコンポーネントでは、Testing Libraryを用いてテストを書いています。
Presentationalコンポーネントから親コンポーネントへの@Outputイベントをモックするのに少し手間取った経験があるため、方法を書き残します。

実際のコード例

todo.component.ts
import { CommonModule } from '@angular/common';
import {
  ChangeDetectionStrategy,
  Component,
  EventEmitter,
  Input,
  Output,
} from '@angular/core';
import { Todo } from '../../models';

@Component({
  selector: 'app-todo',
  standalone: true,
  imports: [CommonModule],
  template: `
    <ul>
      @for (todo of todos; track todo.id) {
        <li>
          <span>Title: {{ todo.title }} | Completed: {{ todo.completed }}</span>
          |
          <button (click)="toggleStatusEvent.emit(todo.id)">Toggle Status</button>
        </li>
      } @empty {
        <p>No todos found</p>
      }
    </ul>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TodoComponent {
  @Input({ required: true }) todos: Todo[] = [];
  @Output() toggleStatusEvent = new EventEmitter<number>();
}
todo.component.spec.ts
import { render } from '@testing-library/angular';
import userEvent from '@testing-library/user-event';
import { TodoComponent } from './todo.component';
import { Todo } from '../../models';

it('should emit toggleStatusEvent when button is clicked', async () => {
  const todos: Todo[] = [{ id: 1, title: 'Test Todo', completed: false }];
  const { component, user, mockToggleStatusEvent } = await setup({ todos });
  const toggleButton = component.getByRole('button', {
    name: /Toggle Status/i,
  });
  await user.click(toggleButton);

  expect(mockToggleStatusEvent.emit).toHaveBeenCalledWith(1);
});

async function setup(props?: Partial<TodoComponent>) {
  const mockToggleStatusEvent = new EventEmitter<number>();
  jest.spyOn(mockToggleStatusEvent, 'emit');

  const component = await render(TodoComponent, {
    componentProperties: {
      todos: props?.todos ?? [],
      toggleStatusEvent: mockToggleStatusEvent
    },
  });

  return {
    component,
    user: userEvent.setup(),
    mockToggleStatusEvent,
  };
}

解説

ポイント1: @Outputイベントをモックする

ts
@Output() toggleStatusEvent = new EventEmitter<number>();
spec.ts
// emit関数をモックする
const mockToggleStatusEvent = new EventEmitter<number>();
jest.spyOn(mockToggleStatusEvent, 'emit');
spec.ts
// componentPropertiesにセットしたモックイベントをreturnする
return {
  // ...
  mockToggleStatusEvent,
};

ポイント2: ボタン要素を取得し、クリックイベントを発火させる

ts
<button (click)="toggleStatusEvent.emit(todo.id)">Toggle Status</button>
spec.ts
  const toggleButton = component.getByRole('button', {
    name: /Toggle Status/i,
  });
  await user.click(toggleButton);

まとめ

このような形で、親コンポーネントへのイベントをモックし、テストすることができました。
明日は @embokoirさんです!

  1. 平日のみの投稿なので、投稿日は11日ですが7日目の記事としています。

11
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?