Узнайте, как нажать на кнопку при модульном тестировании Angular

Кнопки играют большую роль в пользовательском интерфейсе. Angular делает работу с кнопками чрезвычайно простой, но, возможно, вы наткнулись на стену, когда ваше мышление переключилось на тестирование. Должны ли в вашем приложении Angular быть юнит-тесты для нажатия кнопок? Действительно ли это так важно? И если да, то как вы собираетесь тестировать этот сценарий?

Существует два основных подхода к написанию модульных тестов для нажатия на кнопку в Angular: либо вы ищете кнопку в DOM, выполняете фактическое нажатие и проверяете ожидаемое поведение, либо вы просто вызываете код компонента, который будет выполняться при нажатии на кнопку. Оба варианта имеют свои плюсы и минусы. В этой статье мы тщательно исследуем каждый способ тестирования и рассмотрим различные примеры, чтобы вы поняли все, что вам нужно знать о том, как писать модульные тесты для нажатия на кнопку в Angular.

Зачем и когда нужно проводить модульное тестирование нажатий на кнопки в Angular?

Если у вас есть некоторый опыт автоматизированного тестирования, не будет удивительным, если вы зададитесь вопросом, является ли нажатие кнопки чем-то, что вообще нужно обрабатывать с помощью модульных тестов в Angular. Возможно, в прошлом вы предпочитали отказаться от модульных тестов и переложить эту ответственность на E2E (End-to-End) тесты. В этом решении нет ничего плохого — тесты E2E проверяют функциональность, выполняя тесты с точки зрения пользователя, имитируя реальные пользовательские сценарии в приложении.

С другой стороны, модульное тестирование является более детальным. Это автоматизированный фрагмент кода, который вызывает единицу работы (отдельный фрагмент кода) в приложении и обычно рассматривается с точки зрения «черного ящика». Тест проходит или не проходит, основываясь на предположении или ожидании относительно поведения этого блока работы.

🤔 Если вы хотите узнать больше о написании хорошо структурированных и ценных тестов в Angular, ознакомьтесь с моей другой статьей.

Юнит-тест почти всегда пишется с использованием фреймворка тестирования, что позволяет написать его эффективно и быстро запустить. Если вы создаете новый проект Angular с помощью Angular CLI, ваше приложение поставляется с Jasmine и Karma (фреймворк для тестирования и runner) из коробки.

📢 В этой статье предполагается, что вы используете Jasmine и Karma для модульного тестирования. Однако если вы используете другой фреймворк для тестирования, например Jest, подход будет практически идентичным.

Тестирование кнопок Angular: Валидация данных или функциональность приложения

Не существует определенного правила относительно того, следует ли тестировать кнопки. На самом деле, решение о том, писать ли модульный тест для нажатия кнопки, в конечном итоге сводится к личному мнению. Если вы предпочитаете отложить эту функциональность на E2E-тест, это замечательно! Но, на мой взгляд, есть определенные ситуации, когда модульный тест нажатия кнопки дает ценную гарантию в приложении Angular.

Рассмотрим классический пример с калькулятором, который содержит множество кнопок, выполняющих различные математические операции. Каждый раз, когда нажимается кнопка, происходит манипуляция с данными, и на экране появляется новое число или сумма. Это идеальный сценарий для модульного тестирования! Данные меняются с каждым нажатием кнопки; калькулятор выдает определенный результат, когда ему подаются определенные данные.

С другой стороны, нередко кнопка переводит пользователя на другую страницу или заставляет что-то другое появиться или исчезнуть. Эти сценарии не просто изменяют данные, а представляют функциональность приложения и являются отличной возможностью для написания E2E-теста.

Учитывая это, следует ли в вашей ситуации использовать модульный тест или лучше создать E2E-тест?

Напомним, что обычно существует два подхода к написанию модульного теста для кнопок: либо вы размещаете кнопку в DOM и имитируете нажатие, либо вы тестируете код, который будет запущен, когда на кнопку нажмет пользователь. Давайте сначала рассмотрим более сложный пример.

Как протестировать нажатие кнопки в Angular

Этот подход может быть полезен в некоторых ситуациях, но делегирование модульному тесту функций просмотра DOM для поиска кнопки и выполнения щелчка вызывает сомнения. Юнит-тест все еще делает ожидания вокруг понятия того, что должно произойти при нажатии на кнопку, но многие утверждают, что выполнение нажатия является обязанностью E2E-теста.

Независимо от этого, определение местоположения кнопки в DOM является тривиальной задачей, особенно если вы изолируете кнопку в многократно используемый компонент. Ниже приведен пример именно этого — многоразового изолированного компонента кнопки, который, как уже упоминалось, предполагает, что у вас есть TestBed, правильно настроенный с Jasmine и Karma.

describe('Component: Button', () => {
  let fixture: ComponentFixture<ButtonComponent>;
  let component: ButtonComponent;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [ ],
      declarations: [ ButtonComponent ],
      providers: [  ]
    }).compileComponents().then(() => {
      fixture = TestBed.createComponent(ButtonComponent);
      component = fixture.componentInstance;
    });
  }));
});

it('should call onButtonClick when clicked', fakeAsync(() => {
  spyOn(component, 'onButtonClick');

  let button = fixture.debugElement.nativeElement.querySelector('button');
  button.click();

  tick();

  expect(component.onButtonClick).toHaveBeenCalled();
}));
Вход в полноэкранный режим Выход из полноэкранного режима

В файле TypeScript для этого компонента кнопки есть функция onButtonClick, которая привязана к элементу button в шаблоне. Этот тест сначала шпионит за локальной функцией, находит кнопку и затем выполняет щелчок. После имитации прохождения времени с помощью tick() мы утверждаем, что функция onButtonClick была вызвана.

📣 События можно протестировать с помощью функции async / fakeAsync, импортированной из '@angular/core/testing'.

Обратите внимание, что кнопка была расположена в DOM с помощью querySelector и передачи button в качестве аргумента. Это хорошо работает в изолированном компоненте, подобном этому, но в различных сценариях, где может существовать несколько элементов button, вам нужно использовать что-то, что обеспечивает большую конкретность.

Этот пример довольно прост — мы просто проверяем, что функция вызывается при нажатии на кнопку. Но мы можем пойти дальше. Давайте рассмотрим функцию onButtonClick и посмотрим, что еще можно проверить.

@Output() buttonClicked: EventEmitter<any> = new EventEmitter<any>();

...

onButtonClick(): void {
  this.buttonClicked.emit();
}
Вход в полноэкранный режим Выход из полноэкранного режима

Поскольку это многократно используемый компонент кнопки, имеет смысл делегировать ответственность за функциональность тому компоненту, который ее потребляет. Для того чтобы родительский компонент мог определить, когда кнопка была нажата, он может прослушивать эмиттер события внутри компонента кнопки (buttonClicked). В ответ на это событие родительский компонент вызывает локальную функцию, чтобы, например, выполнить математическую операцию в приведенном выше примере с калькулятором.

С точки зрения тестирования было бы полезно иметь модульный тест, который гарантирует, что событие испускается, когда вызывается функция onButtonClick. Вот как это может выглядеть.

it('should emit the event when #onButtonClicked is called', () => {
  const emitSpy = spyOn(component.buttonClickEvent, 'emit');

  component.onButtonClick();

  expect(emitSpy).toHaveBeenCalled();
});
Вход в полноэкранный режим Выход из полноэкранного режима

Здесь на помощь снова приходят шпионы. Функция emit живет в объекте buttonClickEvent, и тест просто проверяет, что шпион был вызван при выполнении тестируемого кода.

Как насчет других ситуаций?

В многократно используемом компоненте кнопки могут быть и другие ситуации, когда модульные тесты могут оказаться полезными и обеспечить уверенность в том, что он будет продолжать работать в будущем при дополнительных изменениях. Однако в этой статье мы не будем обсуждать или рассматривать эти сценарии.

Как протестировать функциональность кнопки в Angular

Теперь, когда у многоразового компонента кнопки есть вспомогательные тестовые случаи, давайте обратим наше внимание на ситуации, в которых может оказаться полезным тестирование локального кода, подключенного к этому компоненту кнопки.

Вспомним, что компонент многоразовой кнопки издает событие при нажатии. Другие части нашего приложения могут слушать это событие и вызывать локальную функцию для выполнения изолированных операций.

Продолжая идею калькулятора, вот один из примеров, где мы используем компонент многоразовой кнопки и слушаем buttonClickEvent.

<app-button (buttonClickEvent)="add(5)">
  5
</app-button>
Вход в полноэкранный режим Выход из полноэкранного режима

У нас уже есть модульные тесты, которые определяют местоположение кнопки в DOM и инициируют событие щелчка, поэтому нет необходимости тестировать это в родительском компоненте. Вместо этого давайте обратимся непосредственно к функции add и посмотрим, есть ли внутри нее что-то, что стоит протестировать.

add(toAdd: number) {
  this.total += toAdd;
}
Вход в полноэкранный режим Выход из полноэкранного режима

Это очень простой и понятный пример специально для этой статьи. Эта функция мутирует данные, и, если вы помните, это отличная возможность добавить вспомогательные тестовые случаи.

Но что вы тестируете?

Для функции add мы напишем тест, который гарантирует, что переменная класса total увеличивается на соответствующее значение, переданное в функцию. Этот пример довольно прост, но умение определять, что тестировать, приходит с практикой.

📣 Если вы хотите развить навык определения тестовых примеров, прочтите мою статью под названием The Gumball Machine — How To Quickly Identify Unit Test Cases.

Вот как будет выглядеть тест. Опять же, это предполагает, что вы правильно настроили набор тестов с помощью TestBed.

it('should add 5 to the calculator total', () => {
  const expectedTotal: number = 10;
  component.total = 5;

  component.add(5);

  expect(component.total).toEqual(expectedTotal);
});
Вход в полноэкранный режим Выход из полноэкранного режима

Обратите внимание, что мы вызываем функцию add непосредственно в тесте родительского компонента. Помните, что у нас уже есть уверенность в том, что кнопка при нажатии работает как положено, поэтому в данном случае мы просто вызываем тестируемый код.

⚠️ Никогда не стоит верить в то, что тест работает так, как задумано, не видя его сбоя. Я рекомендую сделать преднамеренную ошибку в функции add (не в тесте), чтобы проверить, сработает ли тест, когда он должен сработать.

Заключение

В этой статье мы рассмотрели различные способы тестирования нажатия на кнопку в Angular. Один из вариантов — написать модульный тест, который определяет местоположение кнопки в DOM, выполняет щелчок и утверждает, что в тесте что-то произошло. С другой стороны, может быть целесообразно просто вызвать функцию, которая вызывается при нажатии на кнопку, и написать ожидания, основанные на том, что происходит в тестируемом коде.

В конечном счете, все зависит от ваших личных предпочтений. Какой бы путь вы ни выбрали, я надеюсь, что эта статья оказалась полезной и показала вам, как писать модульные тесты для нажатия на кнопку в Angular.

Оцените статью
Procodings.ru
Добавить комментарий