#angular #typescript #unit-testing #jasmine
#angular #typescript #модульное тестирование #jasmine
Вопрос:
Я новичок в Angular и пытаюсь написать 2 модульных теста с использованием Jasmine. Один тест, когда кнопка включена, и один тест, когда кнопка отключена.
Это требование….когда пользователь вводит допустимые данные в текстовое поле, кнопка активируется. Если пользователь вводит неверные данные в текстовое поле, кнопка отключается. Начальное значение отключено. Это текстовое поле и кнопка html:
<input
type="text"
matInput
formControlName = 'triplegOriginControl'
[(ngModel)]="leg.originId"
[matAutocomplete]="autoTripLegOrigin"
class = 'trip-origin-input'
(keydown.Tab)="onKey($event)"
/>
<mat-autocomplete autoActiveFirstOption #autoTripLegOrigin="matAutocomplete">
<mat-option
*ngFor="let option of triplegOriginOptions | async"
[value]="option"
>
{{ option }}
</mat-option>
</mat-autocomplete>
<mat-error *ngIf="triplegForm.controls['triplegOriginControl'].hasError('invalid')">
Please select a valid location
</mat-error>
</mat-form-field>
....
<mat-toolbar color="primary">
<button
mat-raised-button
color="primary"
id="mat-toolbar__get-rate-button-id"
[disabled] = !triplegForm.valid
>
GET RATE
</button>
</mat-toolbar>
У нас есть пользовательский валидатор, который проверяет, есть ли ввод текстового поля в списке местоположений.
Это конструктор для использования функции проверки:
"triplegOriginControl": new FormControl(null, [ ValidateLocation(this.fetchDataService.locationArray)])
Это 2 модульных теста.
describe('Enable or Disable Get Rates Button', () => {
beforeEach(() => {
fetchDataService.locationArray = ["STOC/4958", "NSGR/4143", "ZCRC/416", "NSGR/4143"];
});
it('should enable the button when Origin and Destination are valid', (done) => {
const compiled = fixture.debugElement.nativeElement;
const originIdInput = compiled.querySelector(
".trip-origin-input"
);
originIdInput.value = "STOC/4958";
originIdInput.dispatchEvent(new Event("input"));
const destinationIdInput = compiled.querySelector(
".trip-destination-input"
);
destinationIdInput.value = "NSGR/4143";
destinationIdInput.dispatchEvent(new Event("input"));
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.componentInstance.leg.originId).toBe('STOC/4958');
expect(fixture.componentInstance.leg.destinationId).toBe('NSGR/4143');
expect(compiled.querySelector('#mat-toolbar__get-rate-button-id').disable).toBeFalsy();
done();
});
});
it('should disable the button when Origin and Destination are invalid', (done) => {
const compiled = fixture.debugElement.nativeElement;
const originIdInput = compiled.querySelector(
".trip-origin-input"
);
originIdInput.value = "546";
originIdInput.dispatchEvent(new Event("input"));
const destinationIdInput = compiled.querySelector(
".trip-destination-input"
);
destinationIdInput.value = "NSGxx/43";
destinationIdInput.dispatchEvent(new Event("input"));
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.componentInstance.leg.originId).toBe('546');
expect(fixture.componentInstance.leg.destinationId).toBe('NSGxx/43');
expect(compiled.querySelector('#mat-toolbar__get-rate-button-id').disable).toBeTruthy();
done();
});
});
});
При использовании:
expect(compiled.querySelector('#mat-toolbar__get-rate-button-id').disable)
первый тест (допустимый тест) завершается с ошибкой.
ОШИБКА: ожидаемое значение true равно false.
При использовании атрибута disabled выполняется допустимый тест.
При использовании
expect(compiled.querySelector('#mat-toolbar__get-rate-button-id').disabled)
второй тест (недопустимый тест) завершается с ошибкой.
** Ошибка: ожидается, что значение undefined соответствует действительности.**
При использовании атрибута disable выполняется недопустимый тест.
В чем разница между атрибутом disable и disabled? Код работает так, как ожидалось. Когда пользователь вводит недопустимое значение, кнопка отключается. При вводе допустимого значения кнопка включена. Как исправить эти тестовые примеры
Ответ №1:
Я думаю, что .disable
отключает ввод с использованием JavaScript / HTML, и я не уверен, что .disabled
делает. Я бы проверил атрибут с помощью селектора атрибутов.
https://www.w3schools.com/css/css_attribute_selectors.asp
1.) compiled
по-прежнему ссылается на DOM / HTML до внесения изменений и обнаружения изменений, вам нужна новая ссылка каждый раз, когда происходят изменения.
2.) Используйте [disabled] атрибут CSS selector и посмотрите, когда он присутствует, а когда нет.
Попробуйте это, я добавил комментарии:
it('should enable the button when Origin and Destination are valid', (done) => {
const compiled = fixture.debugElement.nativeElement;
const originIdInput = compiled.querySelector(
".trip-origin-input"
);
originIdInput.value = "STOC/4958";
originIdInput.dispatchEvent(new Event("input"));
const destinationIdInput = compiled.querySelector(
".trip-destination-input"
);
destinationIdInput.value = "NSGR/4143";
destinationIdInput.dispatchEvent(new Event("input"));
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.componentInstance.leg.originId).toBe('STOC/4958');
expect(fixture.componentInstance.leg.destinationId).toBe('NSGR/4143');
// since you did fixture.detectChanges, you need a new reference to the dom
// compiled is still pointing to the previous instance.
const newCompiled = fixture.debugElement.nativeElement;
// attach the attribute selector of [disabled] and expect it to not be there
console.log(!component.triplegForm.valid); // ensure you see false here
expect(newCompiled.querySelector('#mat-toolbar__get-rate-button-id[disabled]')).toBeFalsy();
done();
});
});
it('should disable the button when Origin and Destination are invalid', (done) => {
const compiled = fixture.debugElement.nativeElement;
const originIdInput = compiled.querySelector(
".trip-origin-input"
);
originIdInput.value = "546";
originIdInput.dispatchEvent(new Event("input"));
const destinationIdInput = compiled.querySelector(
".trip-destination-input"
);
destinationIdInput.value = "NSGxx/43";
destinationIdInput.dispatchEvent(new Event("input"));
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.componentInstance.leg.originId).toBe('546');
expect(fixture.componentInstance.leg.destinationId).toBe('NSGxx/43');
// same logic here
console.log(!component.triplegForm.valid); // ensure you see true here
const newCompiled = fixture.debugElement.nativeElement;
expect(newCompiled.querySelector('#mat-toolbar__get-rate-button-id[disabled]')).toBeTruthy();
done();
});
});
Комментарии:
1. Попробовал ваше предложение, и оно завершается ошибкой с другой ошибкой: Ошибка: Ожидаемый <button ngcontent-a-c5 color=»primary» id=»mat-toolbar _get-rate-button-id» mat-raised-button отключен> …</button> чтобы быть ложным.
2. Поиграл и обнаружил, что для действительного теста он пройдет, если я использую это в качестве селектора: #mat-toolbar__get-rate-button-id отключен Но недопустимый тест завершится неудачей, если я использую этот селектор. Если я использую этот селектор #mat-toolbar__get-rate-button-id[отключен] , недопустимый случай проходит
3. Это ложноположительный результат, он проходит, потому что его там нет. Я уверен в селекторе CSS, возможно, есть тип, но
[]
(жесткие скобки) необходимы для селекторов CSS. Проверьте консоль. журналы !component.triplegForm.valid, которые я добавил, и убедитесь, что вы видите точные значения. Если значения точны, возможно, вам нужен другой,fixture.detectChanges()
послеfixture.whenStable()
внутри обратного вызова then.4. Component.triplegForm.valid имеет значение false в обоих случаях. Допустимый флаг использует пользовательский валидатор. Этот валидатор отправляется в службу для получения действительных местоположений. Служба инициализируется: ** beforeEach(() => { fetchDataService. locationArray = [«UPPH/4097», «CACN/4030», «ZCRC/416», «NSGR /4143», «ZCRC/471», «REPH/4987»]; });**
5. Понял это … в массиве местоположений не было введенных мной местоположений. Таким образом, случай был корректно недействительным, поскольку местоположения не были в массиве. БОЛЬШОЕ СПАСИБО ЗА ВАШУ ПОМОЩЬ!