对调用承诺返回函数的递归方法进行单元测试
Posted
技术标签:
【中文标题】对调用承诺返回函数的递归方法进行单元测试【英文标题】:Unit testing a recursive method that calls a promise-returning function 【发布时间】:2017-05-08 05:22:33 【问题描述】:下面是我正在尝试测试的代码的简化版本:一个定期清空的简单队列。对于队列中的每个号码,都会进行 HTTP POST 以将其发送到(在本例中为虚构的)API 地址。如果 POST 成功,则将号码移出队列并考虑下一个号码。
class Queue
queue: Array<number>;
processingQueue: boolean;
constructor(public $http: angular.IHttpService)
this.queue = new Array<number>();
this.processingQueue = false;
startQueueProcessor()
this.processingQueue = false;
setInterval(() =>
if (!this.processingQueue)
this.processQueue();
, 1000);
postNumber(number: number): angular.IHttpPromise<>
return this.$http.post('http://mydummyurl.com/givemeanumber', number);
processQueue(): void
this.processingQueue = true; // we are currently processing a queue, so make sure setInterval does not trigger another processing run
if (this.queue.length !== 0) // are there any numbers to deal with?
const item = this.queue[0]; // get the first number in the queue
this.postNumber(item) // POST it (returns a promise)
.then(
() => // if successful...
this.queue.shift(); // we've dealt with this item, so remove it from the queue
this.processQueue(); // check the queue again (recurses until all items have been dealt with)
,
() => // if unsuccessful...
this.processingQueue = false; // something went wrong, so stop
);
else
this.processingQueue = false; // queue is empty, stop processing
enqueue(number: number): void // add a number to the queue
this.queue.push(number);
我希望创建的测试将检查,在将三个项目添加到队列后,对 processQueue()
的一次调用将清空它。
类似这样的东西(在 Jasmine 中模拟):
describe('queueing', () =>
var queueService;
beforeEach(inject((_$httpBackend_, $injector) =>
httpBackend = _$httpBackend_;
httpBackend.whenPOST().respond(200);
queueService = $injector.get('queue');
));
it('should clear queue completely when processQueue() is called once', () =>
queueService.enqueue(1);
queueService.enqueue(2);
queueService.enqueue(3);
expect(queueService.queue.length).toBe(3);
queueService.processQueue();
// somehow wait for processQueue() to complete
expect(queueService.queue.length).toBe(0);
);
);
我的问题是第二个expect()
总是失败并显示消息Expected 3 to be 0
。我认为这是由于没有等待 postNumber()
返回的承诺,因此在调用第二个 expect()
时队列不为空。
如何等待processQueue()
完成,然后再尝试断言队列已被清空?
【问题讨论】:
【参考方案1】:您应该修改 processQueue 以返回 Promise。最简单的方法是使用 async
关键字标记方法,然后使用 await
:
async processQueue(): Promise<void>
this.processingQueue = true; // we are currently processing a queue, so make sure setInterval does not trigger another processing run
if (this.queue.length !== 0)
// are there any numbers to deal with?
const item = this.queue[0]; // get the first number in the queue
try
await this.postNumber(item); // POST it (returns a promise)
// if successful...
this.queue.shift(); // we've dealt with this item, so remove it from the queue
this.processQueue(); // check the queue again (recurses until all items have been dealt with)
catch
this.processingQueue = false; // something went wrong, so stop
else
this.processingQueue = false; // queue is empty, stop processing
然后在你的测试中:
it('should clear queue completely when processQueue() is called once', async (done) =>
queueService.enqueue(1);
queueService.enqueue(2);
queueService.enqueue(3);
expect(queueService.queue.length).toBe(3);
await queueService.processQueue();
// somehow wait for processQueue() to complete
expect(queueService.queue.length).toBe(0);
done();
);
希望这会有所帮助。
【讨论】:
感谢@Amid 的建议。我已经尝试过了,但出现以下错误:Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
第一个断言正确关闭,但随后await
部分似乎没有返回任何内容并且它超时。
我可以看到两个原因。 http.post 无法到达主机并且操作超时。或者(这更有可能)angular.IHttpPromise 与打字稿使用的通用 A+ 承诺不兼容。转换示例见以下两个链接:codeproject.com/articles/1019920/…和***.com/questions/24611078/…以上是关于对调用承诺返回函数的递归方法进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章
如何模拟在 AngularJS Jasmine 单元测试中返回承诺的服务?