【问题标题】:How to write Jasmine Unit test case for combineLatest rxjs Angular如何为 combineLatest rxjs Angular 编写 Jasmine 单元测试用例
【发布时间】:2019-05-01 11:27:34
【问题描述】:

mycomponent.ts

import { Component, OnInit } from '@angular/core';
import {FormGroup,FormControl} from '@angular/forms'
import { DataServiceService } from './data-service.service';
import {combineLatest,Observable,pipe} from 'rxjs';
import {map,tap} from 'rxjs/operators';
import {Model} from './mode';
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {

  constructor(private dataService: DataServiceService){}
  name = 'Angular';
  myForm: FormGroup;
  observableResult$: Observable<any>;

  ngOnInit(){
    this.myForm = new FormGroup({
      localId: new FormControl()
    })

  this.observableResult$ = combineLatest(
    this.myForm.get('localId').valueChanges,
    this.dataService.getDataFromURL(),
    (localIdSelected, dataFromAPI) => ({localIdSelected,dataFromAPI})).
    pipe(map(each => this.filterData(each.dataFromAPI,each.localIdSelected)));

    this.observableResult$.subscribe(value => {
      debugger
    })

  }
  filterData(dataFromAPI,localIDSelected){
    debugger
     return dataFromAPI.filter(item => item.userId > Number(localIDSelected));
    }

}

data.service.service.ts

import { Injectable } from '@angular/core';
import {HttpClient} from '@angular/common/http'
import {Model} from './mode';
import {Observable} from 'rxjs';
@Injectable()
export class DataServiceService {

  constructor(private http:HttpClient) { }

  getDataFromURL():Observable<Model>{
    return this.http.get<Model>('https://jsonplaceholder.typicode.com/todos');
  }

}

app.component.html

<form [formGroup]="myForm" >

<select formControlName="localId">
  <option>1</option>
  <option>2</option>
</select>

</form>

app.spec.ts

const spyFilter = spyOn(component as any, filterData).and.callThrough();

const constAPIData$ = staticDataServiceMock.getAPIData();
                    spyOn(staticDataServiceMock, 'getAPIData').and.returnValue(
                        observableOf(countryStaticData$)
                    );
component.myForm.get('localId').setValue(1);

component.observableResult$.subscribe(value => {
expect(value[0].id==21).toBeTrue();
});

staticDatamock.ts

export class StaticDataMock{

static getAPIData(): Observable<StaticDataElements[]> {
    return [
  {
    "userId": 1,
    "id": 1,
    "title": "delectus aut autem",
    "completed": false
  },
  {
    "userId": 1,
    "id": 2,
    "title": "quis ut nam facilis et officia qui",
    "completed": false
  },
  {
    "userId": 1,
    "id": 3,
    "title": "fugiat veniam minus",
    "completed": false
  },
  {
    "userId": 1,
    "id": 4,
    "title": "et porro tempora",
    "completed": true
  }];
  }
}

我已经添加了我的测试用例来覆盖 app.spec.ts 中的 combineLatest 运算符 anf filterData,但是所需的代码失败了。我期望调用 filterData 失败了。 combineLatest 将在 valueChange 上触发事件并从 API 获取数据。我可以在 spec 文件中创建 mock 和 setValue,但它仍然无法正常工作。

【问题讨论】:

  • 您的问题是什么?如果您希望有人帮助您编写测试用例,那么您需要非常清楚您到底要测试什么。您还需要显示更多详细信息,例如 mock.getIds() 是什么以及它返回什么,如果您设置了其他模拟及其实现,如果您使用的是 TestBed 的详细信息等,等等.
  • @dmcgrandle:我已经更新了我的问题,请检查
  • 谢谢,我们越来越近了。 :) 我对各种文件的名称有点困惑。 app.component.htmlmycomponent.ts 的模板吗?文件mycomponent.spec.ts 在哪里?此外,请展示整个设置和 TestBed 实施 - 实际上是整个规范文件,而不是正在工作的规范,您不需要帮助。您显示StaticDataMock 类,但在规范中您调用staticDataServiceMock - 但不显示这两者如何(或即使)相关(我猜你将它注入TestBed [providers] 数组......)

标签: rxjs angular2-services angular2-observables combinelatest


【解决方案1】:

好的,为了帮助您继续这项工作,我继续使用您迄今为止提供的数据设置了 Stackblitz。这是link

我做了一些事情来让测试工作(有点)。

  • 我将StaticDataMock 类中getAPIData() 的方法类型更改为public,以便您可以从类外调用它。
  • 我让该方法返回一个 of(),将返回值转换为您的数据的 Observable。
  • 我猜到了您对 DataServiceService 模拟的实现,Stackblitz 中有详细信息。
  • 我创建了一个 Jasmine spyObject 来拦截服务中对 getDataFromURL() 的调用,并返回您使用 StaticDataMock 创建的 observable。
  • 我重新安排了规范中事物的调用顺序,并执行了 console.log() 以表明 component.observableResult$ 永远不会发出。

更新

根据下面的 cmets,上面的 Stackblitz 链接已经更新,现在可以使用。来自 Stackblitz 的工作规范如下:

it('should change return of service.function1() only', () => {
    fixture.detectChanges(); 
    component.observableResult$.subscribe(value => {
        console.log('observable Emitted, value is ', value);
        expect(value[0].id==1).toBe(true); 
    });
    component.myForm.get('localId').setValue(1);
});

关键是先设置订阅,然后在表单中发出一个新值,这将更新combineLatest()并执行subscribe()内的代码。

我很高兴这成功了!

【讨论】:

  • 感谢您的努力!!!我在调试时尝试了你的代码,在期望我得到未定义的订阅值之前。
  • 是的,这就是我在上面讨论中的意思,即在测试过程中很难掌握可观察对象的编码方式。它可能需要重构,但只有你知道这段代码实际上试图完成什么 - 从你到目前为止发布的内容中我不清楚。
  • 我试图使用 combineLatest() 正确链接 observables,我有 2 个控件一个用于国家,另一个用于国家,当国家在第一个控件(combineLatest 的第一个参数)中发生变化时,它应该触发combineLatest 并从国家服务中获取相应的状态(combineLatest 的第二个参数)。第二个参数结果包含对象数组,然后我必须使用选定的国家(使用选定的国家-combineLatest 的第一个参数)对其进行管道和过滤状态
  • stackblitz.com/edit/stackoverflow-q-53538362-ebvdtj 这与您提供的链接几乎相同,无法到达测试用例中的期望块
  • 只需将component.myForm.get('localId').setValue(1); 移动到订阅后即可。需要异步思考——一旦设置了订阅,然后我们用setValue(1)触发一个新的发射,这将导致订阅然后被执行。 :)
猜你喜欢
  • 1970-01-01
  • 2021-07-11
  • 2023-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多