【发布时间】:2020-12-23 20:09:07
【问题描述】:
背景
我有一个 Angular 项目,它有一个用于后端 Web 请求的 Web API,但现在我们需要转换到不同的 API 提供程序。
API 请求都集成到 Angular 服务代码文件中,每个服务的存在都是为了履行特定的职责。
我和我的开发人员同事的任务是创建新的“版本 2”服务,使用相同的方法签名和参数,他们必须调用第二个新 API。
目标是创建具有相同方法和签名的新服务,采用完全相同的参数并产生相同的结果。但是,服务器上的 API 方法签名通常略有不同,其中一些来自新 API 的结果可能需要修改和调整以适应原始格式。
设置
为了帮助实现这一过渡,我们希望创建稍微不那么传统的 Jasmine 单元测试。
对于每个正在转换的服务,我们打算创建一个 ...spec.ts 文件,该文件将执行以下测试和调试的高级要求。
- 初始化测试并使用
TestBed.configureTestingModule,为旧服务和新服务设置适当的提供程序。 - 对于从服务器返回结果的服务上的每个方法,运行
it(...)测试。 - 在测试中,首先调用
MyService.apiRequest()。获取 observable 并获取结果。 - 调用
expect(...)旧结果集以进行完整性检查。 - 致电
MyServiceV2.apiRequest()。获取 observable 并获取结果。 -
expect(...)新结果集是真实的。 - 构造
expect(...)语句,仔细检查旧 API 的结果是否与新 API 结果匹配。
需要明确的是,这些不是我们打算在 CI/CD 管道中使用的测试,也不是为了仔细检查代码。这些是 Jasmine 测试,我们用于快速检查来自我们必须使用的各种 API 的结果集。
问题
我已经尝试过各种方法来设置这样的测试,但我似乎无法从我们的服务中获取结果,等待观察结果并比较数据。我发现的很多 Jasmine 文档都希望您在使用 observable 时通过模拟来伪造结果。这在我们的用例中不起作用。
在我们的例子中,我们的服务调用是合法的,并且是一种从两个特定 API 端点仔细检查我们的数据结果的方法。
为了全面披露,该应用程序目前停留在 Angular 8.2.14 和 Jasmine 2.8.0。下面列出了我为使其正常工作所做的各种尝试。
尝试 #1
it('should get data from MyService.getData(projectId)', ()=>{
//oldSvc and newSvc are references to the old and new services, initialized in beforeEach(...)
//Call to get the first round of data (old data.)
oldSvc.getData(projectId).subscribe((oldData)=>{
expect(oldData).toBeTruthy();
newSvc.getData(projectId, false).subscribe((newData)=>{
expect(newData).toBeTruthy();
expect(newData).toEqual(oldData);
});
});
});
这会返回 Jasmine 成功,但似乎这是因为“SPEC HAS NO EXPECTATION”。我猜测试在可观察订阅者有机会完成之前完成,更不用说调用expect(...) 方法对数据进行任何检查。因此,它看起来很成功,但实际上并没有做任何事情。
尝试 #2
Questions like this one 建议使用first() 方法等待并从每个异步请求中获取第一个结果。我不反对这种方法,但尽管我做出了一切努力,我还是得到了first() 不存在的错误。我添加了对first 的引用(see here 是一个关于如何执行此操作的 SO 问题),但这并不能解决问题。即使添加了导入,first() 也不存在,我得到构建错误,我不得不放弃这种方法。我什至试图用特殊的 TS 标志忽略错误,而不是得到构建错误,我从 Jasmine 中得到first() does not exist 相关错误。这是一个非首发。
尝试 #3
我found this的文章中提到可以等待结果,使用jasmine.clock.tick(...)方法等待结果。通过将文章中的方法集成到您的测试文件中,您可以为您的可观察对象创建“同步”请求。伟大的! ...但这没有用。我的结果总是不确定的,证明它实际上并没有等待结果,即使时钟等待时间很长。
这是本文的重点和对该方法的示例调用。
//Method
function awaitStream(stream$: Observable<any>, skipTime?: number) {
let response = null;
stream$.subscribe(data => {
response = data;
});
if (skipTime) {
/**
* use jasmine clock to artificially manipulate time-based web apis like setTimeout and setInterval
* we can easily refactor this and use async/await but that means that we will have to actually wait out the time needed for every delay/mock request
*/
jasmine.clock().tick(skipTime);
}
return response;
}
//Usage within an it(...) method
...
const oldData = awaitStream(oldSvc.getData(projectId), 10000);
//after calling the method, the result of the observable should be in oldData. It isn't.
...
问题
这些是我为从 Jasmine 中的 API 调用获取异步数据以进行使用和测试所做的各种尝试。在我们当前的 Angular/Jasmine 版本限制下,我无法让这些工作。有没有人有一种直接的方法来调用和等待来自服务返回 Observables 的数据,并在 Jasmine 中检查结果?
谢谢。
【问题讨论】:
标签: angular typescript unit-testing jasmine karma-jasmine