简体   繁体   English

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

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

I am making a very simple application which has one input box and a button.我正在制作一个非常简单的应用程序,它有一个输入框和一个按钮。

  1. Input is for entering email输入用于输入email

  2. Subscribe button with event handler带有事件处理程序的订阅按钮

Entering email and click over the button will make an api call, (This method works)输入 email 并单击按钮将进行 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这里有一个完整的工作示例

There is no issues with it and you can also check everything works fine.它没有问题,您还可以检查一切是否正常。

Requirement: I am in the need to cover test case for this button click and the http call with params.要求:我需要涵盖此按钮单击的测试用例以及带有参数的 http 调用。

I am unable to write the test case and it is showing that the tests are not covered for the http calls with params.我无法编写测试用例,它表明 http 调用的参数未涵盖测试。

在此处输入图像描述

The tests that I have written so for for achieving the button click scenario like,我为实现按钮单击场景而编写的测试,例如,

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

And the Working stackblitz with test cases here以及带有测试用例的工作堆栈闪电战

(Please look into hello.component.spec.ts ) (请查看hello.component.spec.ts

For button click, I have covered and it works对于按钮点击,我已经介绍过并且它有效

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

Could you please help me to achieve the result of covering the test case for the button click that has http call with params?你能帮我实现覆盖带有参数的 http 调用的按钮单击的测试用例的结果吗?

Update: After some search I have found that I can use httpmock to call the url with params but I am ending up with error as,更新:经过一番搜索,我发现我可以使用 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.错误:预期有一个条件匹配请求“匹配 URL: https://gmail.us10.list-manage.com/subscribe/post-json?u=aaa7182511d7bd278,0681.f=1b55&amp;id

You can also see the Modified stackblitz with httpMock here...您还可以在此处查看带有 httpMock 的 Modified stackblitz ...

After all try only I am making this post so please consider this question and provide right solution.毕竟只尝试我正在发布这篇文章,所以请考虑这个问题并提供正确的解决方案。

A big thanks in advance.提前非常感谢。

That was a very long question, I understand that you have put a lot of time to create it.这是一个很长的问题,我知道你花了很多时间来创建它。 So, I have also put some time to refactor the code to make it as per the best practices.因此,我还花了一些时间重构代码以使其符合最佳实践。 This would require to split your code as a service and make the code better for Unit Testing.这需要将您的代码拆分为服务,并使代码更适合单元测试。 (Unit testing for component & service ) 组件服务的单元测试)

  1. Create a service such as MyService and move the http code into it.创建诸如MyService之类的服务并将http代码移入其中。 It would help you unit test component and service separately.它将帮助您分别对componentservice进行单元测试。 I created as below:我创建如下:
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. Create a Unit test for component as you can see here in the stackblitz . 如您在 stackblitz 中所见,为组件创建单元测试。 Take a good look at refactored code & how I have covered all cases depending on the scenario.仔细查看重构代码以及我如何根据场景涵盖所有情况。 As I have already explained in your previous question , you can test MatSnackBar calls正如我在您之前的问题中已经解释的那样,您可以测试MatSnackBar调用

  2. Similarly, you can write Unit test for service by referring to one my of my articles to test a service同样,您可以通过参考我的一篇文章来编写服务的单元测试来测试服务

Indeed it would be better to re-factory the code and make it more test friendly.实际上,最好重构代码并使其对测试更加友好。 But the provided example still could be covered with tests.但是提供的示例仍然可以包含测试。 The idea to verify the passed parameters and if parameters are correct then return a predictable response as an observable.验证传递的参数以及参数是否正确的想法然后返回一个可预测的响应作为可观察的。

so, the this.http.jsonp returns the expected result only if passed parameters are correct.因此,this.http.jsonp 只有在传递的参数正确的情况下才会返回预期结果。

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在这里您可以找到一个工作示例

Please, noticed the I used some extra libs like jasmine-marbles to fake observables or moq.ts to mock HttpClient请注意,我使用了一些额外的库,例如 jasmine-marbles 来伪造 observables 或 moq.ts 来模拟 HttpClient

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM