角度测试用例返回TypeError:_this.reCaptchaApi.render不是函数

时间:2019-01-11 12:42:38

标签: jasmine angular5 karma-jasmine angular-test angular-testing

我已经以Angular 6反应形式实现了ngx-captcha。该功能对我来说很好用,但是当我运行单元测试用例时,测试用例失败并显示以下消息:

  

TypeError:_this.reCaptchaApi.render不是函数。

我在两个组件中实现了ngx-captcha(可见类型)。对于这两个组件,功能对我来说都很好,但是在单元测试用例运行期间,我遇到了上述错误。

我尝试了以下操作:

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();
  });
});

我在Angular 6中使用“ ngx-captcha”:“ ^ 5.0.4”版本。

3 个答案:

答案 0 :(得分:0)

看起来这是一个计时问题。我将您到目前为止发布的内容放入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修改表单,它将无法正常工作。鸡肉和鸡蛋的问题。 :)

我希望这会有所帮助。

答案 1 :(得分:0)

我已通过覆盖包含ngx-recaptcha2组件的模块来解决此问题。

用于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();
  }));

答案 2 :(得分:0)

我认为这是迄今为止最干净的解决方案,只需使用ng-mocks包并在您的导入中模拟NgxCaptchaModule。

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

Protip:模拟所有组件和模块。使用此策略将提高组件中测试的隔离度