【问题标题】:Angular test case returns TypeError: _this.reCaptchaApi.render is not a functionAngular 测试用例返回 TypeError: _this.reCaptchaApi.render is not a function
【发布时间】:2019-06-06 09:25:39
【问题描述】:

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

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

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

我尝试了以下方法:

验证码测试.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" 版本。

【问题讨论】:

  • 请包含您尝试的整个 .spec 文件。
  • 我在问题部分添加了 .spec 文件。谢谢你。 @dmcgrandle

标签: jasmine angular5 karma-jasmine angular-test


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    我已通过覆盖包含 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();
      }));
    

    【讨论】:

      【解决方案3】:

      看起来这是一个时间问题。我将您迄今为止发布的内容放入StackBlitz 以重现您的问题。正如您在 StackBlitz 中看到的,两个测试现在都通过了。

      我对测试套件所做的更改是更改您在第二个beforeEach() 中调用fixture.detectChanges() 的顺序。在更改之前它看起来像这样:

      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 修改表单,它将无法正常工作。鸡和蛋的问题。 :)

      我希望这会有所帮助。

      【讨论】:

      • 这不起作用,我们仍然遇到相同的渲染错误,但有时它正在通过,所以在执行测试时是否有可能出现任何竞争条件?
      • 有趣。它似乎在 StackBlitz 中始终如一地通过。也许您可以将 StackBlitz 分叉到您自己的帐户,并使用您的组件和模板中的所有数据进行更新。该代码中可能存在某些问题。
      猜你喜欢
      • 1970-01-01
      • 2018-07-30
      • 2019-03-22
      • 2018-05-09
      • 1970-01-01
      • 1970-01-01
      • 2021-04-19
      • 2019-02-08
      • 1970-01-01
      相关资源
      最近更新 更多