【问题标题】:Angular2 - Unit Testing Form SubmitAngular2 - 单元测试表格提交
【发布时间】:2017-04-05 00:23:09
【问题描述】:

我有一个简单的组件,它在 form 元素内包含两个输入字段。单击提交按钮时,它会调用组件上的addUser 函数。

组件模板如下:

<div>
  <form [formGroup]="signupForm" (submit)="addUser($event)" role="form" class="form-horizontal">
      <label>Firstname:</label>
      <input type="text" formControlName="firstName"> 
      <label>Lastname:</label>
      <input type="text" formControlName="lastName">
      <input type="submit" id="btnSubmit" class="btn btn-primary btn-lg" value="Register" />
  </form>
</div>

组件定义如下:

@Component({
  moduleId: module.id,  
  templateUrl: 'user.component.html'  
})
export class UserComponent {

  registered = false;

  constructor(
    private router: Router,
    private fb: FormBuilder,
    public authService: AuthService) {

      this.signupForm = this.fb.group({
            'firstName': ['', Validators.required],
            'lastName': ['', Validators.required]
        });        
  }

  addUser(event: any) {
      event.preventDefault();
      this.addUserInvoked = true;
      ......
      ......
      this.authService.register(this.signupForm.value)
        .subscribe(
        (res: Response) => {
            if (res.ok) {
                this.registered = true;
            }
        },
        (error: any) => {
            this.registered = false;                                
        });
  }
}

它工作正常。但是,在我的单元测试中,当我尝试测试在提交按钮上调用单击时,会调用addUser。但不幸的是,addUser 函数没有被调用。

下面是我的示例单元测试

class RouterStub {
  navigateByUrl(url: string) { return url; }
}


let comp: UserComponent;
let fixture: ComponentFixture<UserComponent>;

describe('UserComponent', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [ UserComponent ],
      schemas:      [NO_ERRORS_SCHEMA]
    });
  });

  compileAndCreate();
  tests();
});

function compileAndCreate() {
  beforeEach( async(() => {
    TestBed.configureTestingModule({
      providers: [        
        { provide: Router,      useClass: RouterStub },
        { provide: AuthService, useValue: authServiceStub },
        FormBuilder
      ]
    })
    .compileComponents().then(() => {
      fixture = TestBed.createComponent(UserComponent);
      comp = fixture.componentInstance;
    });
  }));
}

function tests() {
    it('should call addUser when submitted', () => { 
        const spy = spyOn(comp, 'addUser');  

        //*************below method doesn't work and it refreshes the page***************
        //let btnSubmit = fixture.debugElement.query(By.css('#btnSubmit'));
        //btnSubmit.nativeElement.click();

        let form = fixture.debugElement.query(By.css('form'));
        form.triggerEventHandler('submit', null);
        fixture.detectChanges();

        expect(comp.addUser).toHaveBeenCalled();
        expect(authServiceStub.register).toHaveBeenCalled();
        expect(comp.registered).toBeTruthy('user registered'); 
    });

}

我试过了

fixture.debugElement.query(By.css('#btnSubmit')).nativeElement.click()

fixture.debugElement.query(By.css('form')).triggerEventHandler('submit', null)

但我仍然无法调用addUser 函数。我已经在 SO here 上看到了一个问题,但它也没有帮助。

【问题讨论】:

  • 哪一步失败了? expect(comp.addUserInvoked).toBeTruthy(); 是一个不准确的期望,因为如果您实际上没有调用永远不会设置的方法。
  • 这是一个虚拟期望,只是为了测试是否调用了 on submit 函数。
  • 但是那个是失败的吗?因为该函数没有被调用,spy 是,并且你没有调用。你能提供一个minimal reproducible example 的输出吗?
  • 在我的实际应用程序中,我有一个服务依赖项,在我的提交处理程序函数中,我调用了服务函数。我想确保调用服务功能。我已经在 SO 上发布了一个似乎相关的问题。请看这里stackoverflow.com/questions/40672106/…
  • 是的,我明白这一点。我的观点是永远不会调用实际函数。这就是监视它的全部意义所在。所以,最后一次,哪个期望失败了?不要只说“他们都没有工作”,提供有用的具体信息。

标签: unit-testing angular jasmine angular2-forms


【解决方案1】:

这里是示例代码: 1:将 Xcomponent 替换为您的组件名称 2:将 formID 替换为您的表单 ID。

import {async, ComponentFixture, TestBed} from '@angular/core/testing';

    import {FormsModule} from '@angular/forms';
    import {By} from '@angular/platform-browser';

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

      beforeEach(async(() => {
        TestBed.configureTestingModule({
          imports: [FormsModule],
          declarations: [Xcomponent]
        })
          .compileComponents();
      }));

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

      it('should create', () => {
        expect(component).toBeTruthy();
      });

      it('should call save() method on form submit', () => {
        /*Get button from html*/
        fixture.detectChanges();
        const compiled = fixture.debugElement.nativeElement;
        // Supply id of your form below formID
        const getForm = fixture.debugElement.query(By.css('#formID'));
        expect(getForm.triggerEventHandler('submit', compiled)).toBeUndefined();
      });

    });

【讨论】:

    【解决方案2】:
    1. 您需要监视要检查的功能及其依赖的功能。
    2. 调度事件后第二次调用 fixture.detectChanges。
    3. 还要确保您的表单在 dom 上可见,否则查询将返回 null

    我可以这样做:

    let yourService: YourService;
    beforeEach(() => {
        fixture = TestBed.createComponent(YourComponent);
        component = fixture.componentInstance;
        store = TestBed.get(YourService);
        fixture.detectChanges();
    });
    
    
    it('should call the right funtion', () => {       
        spyOn(yourService, 'yourMethod');// or spyOn(component, 'yourMethod');       
        const fakeEvent = { preventDefault: () => console.log('preventDefault') };
        fixture.debugElement.query(By.css('form')).triggerEventHandler('submit', fakeEvent);
        expect(yourService.yourMethod).toHaveBeenCalledWith(
          //your logic here
        );
    });
    

    【讨论】:

      【解决方案3】:

      我遇到了同样的问题,我的解决方案是我必须将“FormsModule”导入到我的测试模块的配置中。

      TestBed.configureTestingModule({
                  imports: [FormsModule]
      });
      

      也许这会有所帮助?

      【讨论】:

        猜你喜欢
        • 2014-06-28
        • 2016-08-08
        • 1970-01-01
        • 2018-01-16
        • 1970-01-01
        • 1970-01-01
        • 2016-10-19
        • 2016-08-07
        • 1970-01-01
        相关资源
        最近更新 更多