Angular单元测试Mock多次HttpClient请求结果

在Angular单元测试中,对于需要Mock的Service,我们可以通过spy来模拟返回结果。

今天遇到一个问题比较尴尬,花了好长时间。在Angular方法里,通过嵌套的HttpClient请求处理数据。但是在进行单元测试的时候,发现如果单纯的模拟post方法,发现,始终只返回最后一次设置的结果。

最终发现,可以通过and.returnValues来返回多个结果。

通过将 spy 与 and.returnValues 链接起来,对该函数的所有调用将按顺序返回特定值,直到它到达返回值列表的末尾,此时它将为所有后续调用返回 undefined。

官方示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
describe("A spy, when configured to fake a series of return values", function() {
var foo, bar;

beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
},
getBar: function() {
return bar;
}
};

spyOn(foo, "getBar").and.returnValues("fetched first", "fetched second");

foo.setBar(123);
});

it("tracks that the spy was called", function() {
foo.getBar(123);
expect(foo.getBar).toHaveBeenCalled();
});

it("should not affect other functions", function() {
expect(bar).toEqual(123);
});

it("when called multiple times returns the requested values in order", function() {
expect(foo.getBar()).toEqual("fetched first");
expect(foo.getBar()).toEqual("fetched second");
expect(foo.getBar()).toBeUndefined();
});
});

实践模拟HttpClient多次请求代码

  • Angular Service
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    getStudents(students: any): Observable<any> {
    return this.postRequest(`api/students`, students).pipe(
    mergeMap(result => {
    return new Observable(subscriber =>{
    if (result) {
    result.map(student => {
    if(student.family === 'ABC'){
    const attributes = students.find(x => x.Id == student.Id).attributes;
    this.postRequest(`api/students/GetBucketIdsByAttributes`, attributes).subscribe(bucketIds =>{
    student.finalBucketIds = bucketIds;
    subscriber.next([student]);
    })
    }
    else {
    subscriber.next([unit]);
    }
    });
    }
    else {
    subscriber.next([]);
    };
    });
    })
    );
    }
  • 在上面的代码中,我们需要模拟两次postRequest的输出结果
  • 测试代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    describe('StudentsService', () => {
    let httpMockMethods = jasmine.createSpyObj(['get', 'post', 'put', 'delete']);
    beforeEach(() => {
    TestBed.configureTestingModule({
    providers: [
    { provide: HttpClient, useValue: httpMockMethods }
    ],
    imports: [
    HttpClientTestingModule
    ]
    });
    httpMock = TestBed.get(HttpTestingController);
    });
    afterEach(() => {
    httpMock.verify();
    httpMockMethods = jasmine.createSpyObj(['get', 'post', 'put', 'delete']);
    });
    it('Should Run HttpClient Request Twice', async () => {
    httpMockMethods.post.and.returnValues(of([{ unitId: '123', family: Families.ymc2, bucketIds: JSON.stringify(valueBucketIds) }]), of(bucketId));
    projectTreeService.getBucketIds(request).subscribe({
    next: result => {
    expect(result[0].finalBucketIds).toEqual(bucketId);
    }
    });
    expect(httpMockMethods.post.calls.count()).toBe(2, 'two call');
    }
    })

引用

发布