Угловой тестовый пример возвращает TypeError: _this.reCaptchaApi.render не является функцией - PullRequest
0 голосов
/ 11 января 2019

Я реализовал ngx-капчу в Angular 6 Reactive Forms. Функциональность у меня работает нормально, но когда я запускаю модульный тестовый пример, он завершается неудачно со следующим сообщением:

TypeError: _this.reCaptchaApi.render не является функцией.

Я реализовал ngx-captcha (видимый тип) в двух компонентах. Оба компонента работают нормально, но во время выполнения модульного теста я получаю вышеупомянутую ошибку.

Я пробовал со следующим:

каптч test.html

<ngx-recaptcha2 #captchaElem [siteKey]="captchaSiteKey" 
formControlName="captcha"> </ngx-recaptcha2>

captca-test.ts

export class CaptchaTestComponent implements OnInit {
  validSiteKey: string = environment.validSiteKey;
  captchaExampleForm: FormGroup;
  constructor(private fb: FormBuilder) { }

  ngOnInit() {
    this.captchaExampleForm = this.fb.group({
      captcha: ['', [Validators.required]]
    });
  }

exampleFormSubmit(){
return false;
   }
}

// Captcha-test.spec.ts

import { async, ComponentFixture, TestBed } from 
"@angular/core/testing";
import { HttpClientModule } from "@angular/common/http";
import { CaptchaTestComponent } from "./captcha-test.component";
import { BrowserAnimationsModule } from "@angular/platform- 
browser/animations";
import { RouterTestingModule } from "@angular/router/testing";
import { MatProgressSpinnerModule, MatFormFieldModule, MatInputModule } 
from "@angular/material";
import { ReactiveFormsModule, FormBuilder } from "@angular/forms";
import { TestConstants } from "src/app/test/constants";
import { NgxCaptchaModule } from "ngx-captcha";

describe("CaptchaTestComponent", () => {
jasmine.getEnv().allowRespy(true);
let fixture: ComponentFixture<CaptchaTestComponent>;

beforeEach(async(function () {
TestBed.configureTestingModule({
  imports: [RouterTestingModule, BrowserAnimationsModule,
    ReactiveFormsModule, HttpClientModule, MatProgressSpinnerModule,
    MatFormFieldModule, MatInputModule, NgxCaptchaModule],
  providers: [FormBuilder
  ],
  declarations: [CaptchaTestComponent]
  }).compileComponents();
}));

beforeEach(function () {
 fixture = TestBed.createComponent(CaptchaTestComponent);
 this.component = fixture.componentInstance;
 this.component.captchaExampleForm.controls["captcha"].setValue(TestConstan ts.validCaptcha); //manually set a string data as input data.

fixture.detectChanges();
});

it("should init component properly", function () {
  this.component.ngOnInit();
  expect(this.component.captchaExampleForm).toBeDefined();
});

it("should return submit as false when we submit the form", async 
  function () {
    const result = await this.component.exapmleFormSubmit();
    expect(result).toBeFalsy();
  });
});

Я использую "ngx-captcha": версия "^ 5.0.4" с Angular 6.

Ответы [ 3 ]

0 голосов
/ 24 января 2019

Я исправил эту проблему, переопределив модуль, содержащий компонент ngx-recaptcha2.

MockComponent для Recaptcha V2

import { Component, Input, forwardRef } from "@angular/core";
import { NG_VALUE_ACCESSOR } from "@angular/forms";
import { MockControlValueAccessor } from "./control_value_accessor.mock";

@Component({
    // tslint:disable-next-line:component-selector
    selector: "ngx-recaptcha2",
    template: "<div></div>",
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => MockRecaptchaV2Component),
            multi: true
        },
    ]
})
export class MockRecaptchaV2Component extends MockControlValueAccessor {

    @Input() siteKey: string;
}
<ngx-recaptcha2 #captchaElem [siteKey]="captchaSiteKey" formControlName="captcha"></ngx-recaptcha2>

Recaptcha использует formControlName, поэтому нам нужен следующий фиктивный класс.

import { ControlValueAccessor } from "@angular/forms";

export class MockControlValueAccessor implements ControlValueAccessor {
    writeValue(obj: any): void {
    }
    registerOnChange(fn: any): void {
    }
    registerOnTouched(fn: any): void {
    }
    setDisabledState?(isDisabled: boolean): void {
    }
}

Теперь нам нужно переопределить модуль NgxCaptchaModule, используя следующий код.

beforeEach(async(function () {
    TestBed.configureTestingModule({
      imports: [BrowserAnimationsModule, ReactiveFormsModule, NgxCaptchaModule],
      declarations: [SignupComponent, MockRecaptchaV2Component],
    }).overrideModule(NgxCaptchaModule, {
      remove: {
        // SignupComponent is needed as ReCaptcha2Component is used as child component inside SignupComponent
        declarations: [SignupComponent, ReCaptcha2Component], 
        exports: [SignupComponent, ReCaptcha2Component]
      }
    }).compileComponents();
  }));
0 голосов
/ 08 июня 2019

На мой взгляд, это самое чистое решение, просто используйте пакет ng-mocks и смоделируйте модуль NgxCaptchaModule в ваших импорте.

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        // other imports
        MockModule(NgxCaptchaModule),
      ],
      declarations: [
        SignUpComponent
      ]
    }).compileComponents();
  }));

Protip: макет всех ваших компонентов и модулей. Использование этой стратегии увеличит изоляцию ваших тестов в компонентах

0 голосов
/ 14 января 2019

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

Изменение, которое я внес в набор тестов, состояло в том, чтобы изменить порядок вызова fixture.detectChanges() во втором beforeEach(). До изменения это выглядело так:

beforeEach(function () {
  fixture = TestBed.createComponent(CaptchaTestComponent);
  this.component = fixture.componentInstance;
  this.component.captchaExampleForm.controls["captcha"].setValue(TestConstants.validCaptcha); //manually set a string data as input data.
  fixture.detectChanges();
});

А после изменения это выглядит так:

beforeEach(function () {
  fixture = TestBed.createComponent(CaptchaTestComponent);
  this.component = fixture.componentInstance;
  fixture.detectChanges();
  this.component.captchaExampleForm.controls["captcha"].setValue(TestConstants.validCaptcha); //manually set a string data as input data.
});

Причина, по которой это важно, заключается в том, что fixture.detectChanges() вызывает ngOnInit(), и вы настраиваете свою форму внутри ngOnInit(). Если вы попытаетесь изменить форму с помощью setValue до ее настройки, она не будет работать. Курица и яйцо проблема. :)

Надеюсь, это поможет.

...