如何覆盖 angular 中按钮点击的测试? (附 Stackblitz)

How to cover test for button click in angular? (Stackblitz attached)

我正在制作一个非常简单的应用程序,它只有一个输入框和一个按钮。

  1. Input 用于输入 email

  2. 使用事件处理程序

  3. 订阅按钮

输入电子邮件并点击按钮将进行 api 呼叫,(此方法有效)

  subscribeEmail() {
    this.error = '';


        if (this.userForm.controls.email.status === 'VALID') {

      const params = new HttpParams()
                .set('EMAIL', this.userForm.controls.email.value)
        .set('subscribe','Subscribe')
        .set('b_aaa7182511d7bd278fb9d510d_01681f1b55','')
      console.log(params);
            const mailChimpUrl = this.mailChimpEndpoint + params.toString();

            this.http.jsonp<MailChimpResponse>(mailChimpUrl, 'c').subscribe(response => {
        console.log('response ', response)
                if (response.result && response.result !== 'error') {
                    this.submitted = true;
                }
                else {
                    this.error = response.msg;
                }
            }, error => {
                console.error(error);
                this.error = 'Sorry, an error occurred.';
            });
        }
  }

A complete working example here

没有问题,您也可以检查一切正常。

要求:我需要覆盖此按钮点击和带参数的 http 调用的测试用例。

我无法编写测试用例,它显示测试未涵盖带参数的 http 调用。

我为实现按钮点击场景而编写的测试,

describe('HelloComponent', () => {

  let component: HelloComponent;
  let fixture: ComponentFixture<HelloComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
       imports: [
        HttpClientTestingModule,
        ReactiveFormsModule
      ],
      declarations: [HelloComponent]
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(HelloComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('check subscribeEmail function has value', (done: DoneFn) => {

    const subscribeButton = fixture.debugElement.query(By.css('.subscribe'));
    subscribeButton.triggerEventHandler('click', {});

    done();
  });
});

还有 Working stackblitz with test cases here

(请看hello.component.spec.ts)

对于按钮点击,我已经介绍过了并且有效

    const subscribeButton = fixture.debugElement.query(By.css('.subscribe'));
    subscribeButton.triggerEventHandler('click', {});

你能帮我实现覆盖带有参数的http调用的按钮点击的测试用例的结果吗?

更新: 经过一番搜索后,我发现我可以使用 httpmock 来调用带有参数的 url,但最终出现错误,

Error: Expected one matching request for criteria "Match URL: https://gmail.us10.list-manage.com/subscribe/post-json?u=aaa7182511d7bd278fb9d510d&id=01681f1b55&amp", found none.

您还可以看到 Modified stackblitz with httpMock here...

毕竟只有我在做这个post所以请考虑这个问题并提供正确的解决方案。

非常感谢。

这是一个很长的问题,我知道你花了很多时间来创建它。因此,我也花了一些时间来重构代码以使其符合最佳实践。这将需要将您的代码拆分为服务,并使代码更好地进行单元测试。 (component & service 的单元测试)

  1. 创建服务,例如 MyService 并将 http 代码移入其中。它将帮助您分别对 componentservice 进行单元测试。我创建如下:
export class MyService {
  mailChimpEndpoint = 'https://gmail.us10.list-manage.com/subscribe/post-json?u=aaa7182511d7bd278fb9d510d&amp;id=01681f1b55&amp';
  constructor(private _http: HttpClient) {}

  submitForm(email: string){
    const params = new HttpParams()
                .set('EMAIL', email)
        .set('subscribe','Subscribe')
        .set('b_aaa7182511d7bd278fb9d510d_01681f1b55','')
    const mailChimpUrl = this.mailChimpEndpoint + params.toString();

    return this._http.jsonp<MailChimpResponse>(mailChimpUrl, 'c')
  }
}
  1. 为组件 as you can see here in the stackblitz . Take a good look at refactored code & how I have covered all cases depending on the scenario. As I have already explained in 创建单元测试,您可以测试 MatSnackBar 调用

  2. 同样可以参考one my of my articles to test a service

  3. 编写服务的单元测试

确实,重构代码并使其更易于测试会更好。 但是提供的例子仍然可以用测试来覆盖。验证传递的参数的想法,如果参数正确,则 return 作为可观察的可预测响应。

因此,只有当传递的参数正确时,this.http.jsonp return 才是预期的结果。

import { ComponentFixture, TestBed, async, fakeAsync, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement, Injector }  from '@angular/core';

import { HelloComponent } from './hello.component';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { moqInjectorProviders, resolveMock } from 'ng-auto-moq';
import { Mock, It } from 'moq.ts';
import { HttpClient, HttpParams } from '@angular/common/http';
import { cold, getTestScheduler } from 'jasmine-marbles';


describe('HelloComponent', () => {
  let component: HelloComponent;
  let fixture: ComponentFixture<HelloComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
       imports: [
        HttpClientTestingModule,
        ReactiveFormsModule
      ],
      declarations: [HelloComponent],
      // setups automatically all dependecies of the component as mock objects
      providers: moqInjectorProviders(HelloComponent)
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(HelloComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('check subscribeEmail function has value', fakeAsync(() => {
    const injector = TestBed.get(Injector);

    // sets mock behaviour
    resolveMock(HttpClient, injector)
      // if jsonp is invoked with first parameter with email substring
      .setup(instance => instance.jsonp(It.Is<string>(url => url.indexOf('me@gmail.com') > 1), 'c'))
      // only in this case return a cold observable
      .returns(cold("a", {a: {result: "success"}}));

    const email = fixture.debugElement.query(By.css('.email-input')).nativeElement;
    // sets email value
    email.value = 'me@gmail.com';
    // notifies angualr that element has changed
    email.dispatchEvent(new Event('input'));

    const subscribeButton = fixture.debugElement.query(By.css('.subscribe'));
    subscribeButton.triggerEventHandler('click', {});

    // flush the observable of jsonp
    getTestScheduler().flush();
    fixture.detectChanges();

    expect(component.submitted).toBe(true);
  }));

  it('sets error when server returns an error', fakeAsync(() => {
    const injector = TestBed.get(Injector);
    const msg = "erorr message";

    resolveMock(HttpClient, injector)
      .setup(instance => instance.jsonp(It.Is<string>(url => url.indexOf('me@gmail.com') > 1), 'c'))
      .returns(cold("a", {a: {result: "error", msg}}));

    const email = fixture.debugElement.query(By.css('.email-input')).nativeElement;
    email.value = 'me@gmail.com';
    email.dispatchEvent(new Event('input'));

    const subscribeButton = fixture.debugElement.query(By.css('.subscribe'));
    subscribeButton.triggerEventHandler('click', {});

    getTestScheduler().flush();
    fixture.detectChanges();

    expect(component.error).toEqual(msg);
  }));
});

Here you can find an working example

请注意,我使用了一些额外的库,例如 jasmine-marbles 来伪造可观察对象或 moq.ts 来模拟 HttpClient