Тест Angular Реактивные формы с использованием Rx JS Мрамор - PullRequest
7 голосов
/ 17 апреля 2020

Angular Компонент

public setupObservables() {
  this.formFieldChanged$ = this.formField
    .valueChanges
    .pipe(
        debounceTime(100),
        distinctUntilChanged((a, b) => a === b),
    )
}

Жасминовый тест

import { of } from 'rxjs';
import { marbles } from 'rxjs-marbles/jasmine';  
...

it('should update value on debounced formField change', marbles(m => {
  const values = { a: "1", b: "2", c: "3" };

  const fakeInputs = m.cold('a 200ms b 50ms c', values);
  const expected = m.cold('100ms a 250ms c', values);

  // works on stackblitz but otherwise gives TS2540 compiler error
  // cannot assign to a read-only property
  component.formField.valueChanges = fakeInputs; 
  component.setupObservables();

  m.expect(component.formFieldChanged$).toBeObservable(expected);
}));

Пример stackblitz.com

Цель - использовать мраморные тесты проверить Observable код в контексте с Angular реактивными формами.

  • Имеет ли этот подход смысл?
  • Как лучше всего высмеивать valueChanges объекта FormField ?
  • Есть ли лучший способ структурировать такого рода тесты?

1 Ответ

5 голосов
/ 25 апреля 2020

вопрос в том - что вы хотите проверить. это юнит тест или e2e тест? если это юнит-тест - имитируйте реактивные формы, покрывайте только свои логи c, тогда у вас нет проблем с valueChanges, потому что он по ошибке и вы управляете им.

, если это тест e2e - вы не должны переназначать valueChanges. Ничто не должно быть подделано / заменено, потому что это тест e2e.

Тем не менее, если вы хотите изменить valueChanges - используйте https://github.com/krzkaczor/ts-essentials#writable

(Writable<typeof component.formField>component.formField).valueChanges = fakeInputs; 

Это будет сделайте тип свойства доступным для записи.

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

Внедрение частей, которые мы хотим смоделировать

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

@Component({
    selector: 'app-component',
    templateUrl: './app-component.html',
    styleUrls: ['./app-component.scss'],
    providers: [
        {
            provide: 'form',
            useFactory: () => new FormControl(),
        },
    ],
})
export class AppComponent {
    public formFieldChanged$: Observable<unknown>;

    constructor(@Inject('form') public readonly formField: FormControl) {
    }

    public setupObservables(): void {
        this.formFieldChanged$ = this.formField
            .valueChanges
            .pipe(
                debounceTime(100),
                distinctUntilChanged((a, b) => a === b),
            );
    }
}

Тогда вы можете просто ввести вместо теста макет вместо него.

it('should update value on debounced formField change', marbles(m => {
    const values = { a: "1", b: "2", c: "3" };

    const fakeInputs = m.cold('a 200ms b 50ms c', values);
    const expected = m.cold('100ms a 250ms c', values);

    const formInput = {
        valueChanges: fakeInputs,
    };

    const component = new AppComponent(formInput as any as FormControl);

    component.setupObservables();
    m.expect(component.formFieldChanged$).toBeObservable(expected);
}));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...