Угловое тестирование и ngModel

Я пишу один из моих первых тестов компонентов с Angular, и у меня есть некоторые трудности, чтобы заставить работать привязку ngModel. Вот мое определение тестового модуля:

beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        LdapLoginComponent,
      ],
      imports: [
        CommonModule,
        FormsModule,
        NoopAnimationsModule,
        MatInputModule,
        MatFormFieldModule,
        RouterTestingModule,
      ],
      providers: [
        {
          provide: AuthorizationService,
          useValue: { login() {} },
        },
      ]
    }).compileComponents();
  }));

И вот мой тестовый пример:

it('should bind form fields with class', fakeAsync(() => {
    // Given
    const username = 'username';
    const password = 'password';
    const usernameField = de.query(By.css('input[name=username]')).nativeElement;
    const passwordField = de.query(By.css('input[name=password]')).nativeElement;

    // When
    usernameField.value = username;
    passwordField.value = password;
    usernameField.dispatchEvent(new Event('input'));
    passwordField.dispatchEvent(new Event('input'));
    tick();
    fixture.detectChanges();

    // Then
    expect(comp.username).toEqual(username);
    expect(comp.password).toEqual(password);
  }));

Мой класс компонентов:

export class LdapLoginComponent {

  username: string;
  password: string;
  errorMessage: string;
  submitDisabled = false;

  constructor(
    private authorizationService: AuthorizationService,
    private router: Router,
  ) {
  }

  login(): void {
    delete this.errorMessage;
    this.submitDisabled = true;
    this.authorizationService.login(AuthorizationProvider.LDAP, this.username, this.password)
      .subscribe(
        () => {
          this.router.navigate(['/']);
        },
        (err: Error) => {
          this.errorMessage = err.message;
          this.submitDisabled = false;
        },
      );
  }

}

И мой компонент шаблона:

<form class="form-container" (submit)="login()">
  <mat-form-field color="warn">
    <input
    matInput
    type="text"
    name="username"
    placeholder="Insert your username"
    [(ngModel)]="username"
    required
    i18n-placeholder="@@input.placeholder.username">
  </mat-form-field>
  <mat-form-field color="warn">
    <input
      matInput
      type="password"
      name="password"
      placeholder="Insert your password"
      [(ngModel)]="password"
      required
      i18n-placeholder="@@input.placeholder.password">
  </mat-form-field>
  <button
    mat-raised-button
    type="submit"
    color="warn"
    [disabled]="submitDisabled"
    i18n="@@input.submit">Submit</button>
</form>
<article>{{errorMessage}}</article>

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

Есть идеи?

Благодарю.

2 ответа

Решение

Похоже, для полей ввода mat, мы должны сделать focus() перед изменением ввода:

it('should bind form fields with class', fakeAsync(() => {
// Given
const username = 'username';
const password = 'password';
const usernameField = de.query(By.css('input[name=username]')).nativeElement;
const passwordField = de.query(By.css('input[name=password]')).nativeElement;

usernameField.focus();
passwordField.focus();

// When
usernameField.value = username;
passwordField.value = password;
usernameField.dispatchEvent(new Event('input'));
passwordField.dispatchEvent(new Event('input'));
tick();
fixture.detectChanges();

// Then
expect(comp.username).toEqual(username);
expect(comp.password).toEqual(password);

}));

Проблема в том, что вы на самом деле не устанавливаете значение поля ввода перед вызовом dispatchEvent. Вы устанавливаете атрибут компонента напрямую.

comp.username = username;
usernameField.dispatchEvent(new Event('input'));

Должно быть

let usernameFieldElement = usernameField.nativeElement;
usernameFieldElement.value = username;
usernameField.dispatchEvent(new Event('input'));

и то же самое для пароля.

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

  1. Установите поля, как указано выше, и проверьте привязку данных.

  2. Нажмите кнопку и шпионите за функцией входа, чтобы проверить, была ли она вызвана.

  3. Установите фактические атрибуты компонента и вызовите logon() напрямую и убедитесь, что ваш navigateSpy был вызван правильно.

Таким образом, если что-то пойдет не так, вы сможете найти это намного легче.

Другие вопросы по тегам