我正在尝试使用测试库node-jasmine对下面的函数进行单元测试:
joinGame(participant) {
console.log('Joining game', participant);
if (this.getParticipants().length >= MAX_NUMBER_OF_PARTICIPANTS) {
throw new Error(`The game with id ${this.getId()} has reached the maximum amount of participants, ${MAX_NUMBER_OF_PARTICIPANTS}`);
}
this.addParticipant(participant);
this.incrementPlayerCount();
this.emit(actions.GAME_JOINED, participant);
// Is the game ready to start?
if (this.playerCount >= REQUIRED_NUMBER_OF_PARTICIPANTS) {
// Start game loop by initializing the first round
this.createRound();
}
}
但是,当单元测试函数时,一些代码路径引导我调用位于函数末尾的'this.createRound()'。 createRound()基本上初始化游戏循环,启动计时器,以及与我单元测试功能完全无关的其他副作用。看看下面的测试:
it('should throw an error if a user tries to join a game with the maximum amount of participants has been reached', () => {
game = new Game();
// To test whenever there are two participants in the game
game.joinGame(hostParticipant);
game.joinGame(clientParticipant);
function testJoin() {
game.joinGame(joiningParticipant);
}
expect(testJoin).toThrow();
});
现在当我运行测试时,测试将反对我的意志调用'createRound()'。 'createRound()'实例化Round实例并启动倒数计时器,这使得我的命令行中的'npm test'调用永远不会完成。由于测试认为它是测试的一部分。
以下是我想到并实施的一些方法。虽然,我觉得他们中的任何一个都不“干净”,这就是为什么我在寻找你的意见。
方法1:在测试中存储'createRound()'以替换其功能。这工作正常,但它是避免调用副作用的正确方法吗?
方法2:尝试在beforeEach / afterEach之前设置/拆除Game实例。我尝试过这种方法没有成功。但是,通过在'afterEach()'上将游戏实例设置为null,实例化的圆形实例将继续与其计时器一起运行。
方法3:在调用'joinGame()'并使用Round实例时使用依赖注入。这没有多大意义,因为在调用'joinGame()'时,客户端不应该提供新的圆形实例。此外,并非每次调用'joinGame()'都会调用'createRound()';只有当玩家数量超过所需的玩家数量时。
咬住createRound
肯定是有道理的。您正在编写一个测试来断言拒绝用户加入完整游戏的行为,而不是计时器是否按预期工作。如果你在测试对象上存在一个方法,这会有点毛茸茸,但后来我认为管理计时器的逻辑可能属于它自己的独立对象。
当然,你也可以考虑:
方法4:按照茉莉花documentation中的描述模拟时钟。假设定时器依赖于setTimeout
/ setInterval
,您可以在调用函数之前安装假时钟,并手动勾选时钟以获取可以进行断言的状态。