Angular 2+ w / Jasmine测试:[ngClass] =“methodName(configObj)”问题

时间:2018-05-14 13:26:28

标签: angular unit-testing jasmine

我已经阅读了整个angular.io测试文档,搜索了关键字并使用了S.O.的高级搜索。但没有发现任何适用于我的情况。我是单元测试的新手,所以也许我还没有这个术语。如果这是重复的,请指出我正确的方向。

预示:我的问题是

A。)angular / jasmine是否能够从模板方法调用中测试动态参数。

或者......

B。)在每个fixture.detectChanges()中你如何模拟传递参数到方法?

这是情况和失败的代码......

<generic-element 
  *ngFor=”let recordObj of recordsArray” 
  [ngClass]=”doSetClasses(recordObj)”
></generic-element>

总之,我有一个组件模板,其中一个元素动态地获取它通过方法分配的类。此方法接受一个对象(通过ngFor指令从列表中提取)并检查它以确定它应返回给模板的类名。我更喜欢通过方法分配真正详细的分配,因为我试图将我的逻辑保留在typescript文件和HTML中的结构......至少尽可能多。

我非常简单的单元测试失败了,因为“无法读取未定义的属性'id'(在这种情况下未定义的是从模板传递到doSetClasses()方法的recordObj。我已经将它隔离了,因为如果我只是覆盖并去除测试环境中doSetClasses方法的内容,一切都通过,例如:

beforeEach {
   ...
   component.doSetClasses = function() { return; } as any;
}

现在如果我按原样保留doSetClasses方法,我注意到如果我不调用fixture.detectChanges()它也会通过。所以我的问题似乎是:测试方法,从模板中调用,在角度变化检测/摘要周期中也包含模板中包含的参数。

我的问题是

A。)angular / jasmine是否能够从模板方法调用中测试动态参数。

或者......

B。)在测试环境中的每个fixture.detectChanges()期间,你如何模拟将参数传递给方法?

同样,如果这是重复的,请指出我正确的方向。

提前感谢您的任何帮助,建议和指导。非常感谢。

2 个答案:

答案 0 :(得分:0)

在几天没有答案,评论或问题之后,似乎没有提交任何指导。所以对于今后可能会发现类似问题的人来说,这就是我所得到的:

  1. 我不相信有一种方法可以有机地拥有[ngClass] =&#34; doSetClasses(recordObj)&#34;,这被称为每个摘要周期,从中动态获取它的参数* ngFor,在单元测试中工作,因此我不得不嘲笑它。
  2. 在测试环境中创建doSetClasses()方法的副本。
  3. 然后删除原始doSetClasses()方法的内容,这样它仍然可以被代码/其他测试调用,但最终完全没有做任何事情。
  4. 在测试doSetClasses本身的实际使用时,请改用副本并模拟场景。
  5. 我目前无权访问我的代码,但这是简写:

    beforeEach() {
      //Create a copy to use in controlled scenarios
      const doSetClassesCopy = doSetClasses;
      //Then strip out contents so it can be called but does not error during testing
      doSetClasses = function() { return; } as any;
    }
    
    ...
    (The Test)
      //Now mock it's performance with params from template's *ngFor directive
      const simulatedParamsFromTemplate = { value: 'whatever' } as recordObj;
      const outcome = doSetClassesCopy(simulatedParamsFromTemplate);
      expect(outcome).toBe('Whatever I expect.');
    

    如果有人有更好的解决方案或建议,我有兴趣听取它。感谢。

答案 1 :(得分:0)

我今天也遇到了类似的问题。调用fixture.detectChanges()似乎会触发调用ngClass中提到的函数。

我想调用validateField,它将把Bootstrap的类分配给控件

HTML

<form class="form-inline" [formGroup]="loginForm" (ngSubmit)="signInUser()" novalidate>
            <input type="text" id="username" class="form-control" placeholder="username" formControlName="userName" [ngClass]="validateField('userName')" required>
            <input type="password" id="password" class="form-control" placeholder="password" formControlName="password" [ngClass]="validateField('password')" required>
...

validateField中的.ts函数是

/*

  This function will eventually return object of type depending on whether the field is valid or not, set Bootstrap's css
   {
      'is-invalid': true or false, // if fieldvalid returns false i.e. field is not valid then we want has-error css. So set it to true (opp. of fieldValid)
      'is-valid': true or false //if fieldvalid returns true i.e. field is valid then we want has-success css. So set it to true (value of fieldValid)
   }
   */
  validateField(field:string){
   // console.log("validating field: " +field)
    return this.helper.validateField(this.loginForm,field);
  }

我的spec

fit('it should validate that the data in username and password field has correct format',()=>{
    let navComponent = component;
    let helperService = TestBed.get(HelperService);
    spyOn(helperService,'validateField');
    let formControls = navComponent.loginForm.controls;
    let email = 'test@test.com';
    let password = 'testpassword';
    formControls['userName'].setValue(email);
    fixture.detectChanges(); //<-- THIS SEEM TO TRIGGER THE CALLING OF VALIDATION FUNCTION
    expect(helperService.validateField).toHaveBeenCalledWith(navComponent.loginForm,'userName');
    formControls['password'].setValue(password);
    fixture.detectChanges();//<-- THIS SEEM TO TRIGGER THE CALLING OF VALIDATION FUNCTION
    expect(helperService.validateField).toHaveBeenCalledWith(navComponent.loginForm,'password');

  });