桌游掷骰子逻辑单元测试随机失败问题排查求助
1. 测试循环的逻辑条件完全错误
你的测试循环条件while (game.getThrowsInCurrentTurn() != 2 && game.getDoublesInCurrentTurn() != 1)完全偏离了预期的测试场景。这个条件的意思是:只有当既不是第2次掷骰,又从未掷出过重骰时,才继续循环。
当第一次掷骰就抛出重骰(throws=1, doubles=1)时,doubles!=1为false,整个&&条件结果为false,循环直接终止。但此时你只完成了一次掷骰,还没执行第二次掷骰操作!接下来的ASSERT_EQ(game.canThrow(), false)断言自然会失败——因为此时游戏处于“掷出重骰,可再次掷骰”的状态,m_canThrow是true。
这就是测试随机失败的核心原因:当第一次掷骰就命中重骰时,测试提前终止,没有走到第二次掷骰的流程。
2. 游戏逻辑的边界场景未覆盖
你的checkForDoubles()函数缺少对连续两次非重骰(m_doublesInCurrentTurn == 0 && m_throwsInCurrentTurn == 2)场景的处理。虽然这不是当前测试的目标,但如果测试流程意外进入这个场景,m_canThrow和m_canMove的状态会保持之前的值,可能导致其他断言异常。
第一步:重构测试逻辑,精准控制场景
重新梳理测试流程,明确我们需要的是“第一次掷重骰,第二次非重骰”的场景,用更清晰的循环逻辑确保进入目标场景:
TEST_F(GameTestSuite, shouldFinishAfterSecondRollAndMove) { auto game = m_sut.get(); bool validScenario = false; while (!validScenario) { game.reset(); // 第一次掷骰,直到掷出重骰 game.rollTheDice(); game.checkForDoubles(); if (game.getDoublesInCurrentTurn() != 1) { continue; } // 第二次掷骰 game.rollTheDice(); game.checkForDoubles(); // 验证是否是目标场景:第一次重骰、第二次非重骰 if (game.getDoublesInCurrentTurn() == 1 && game.getThrowsInCurrentTurn() == 2) { validScenario = true; } // 如果第二次也掷出重骰,会触发入狱流程,直接重置重来 } // 执行核心断言 ASSERT_EQ(game.canThrow(), false); ASSERT_EQ(game.canMove(), true); game.setInMotion(game.getTotalRollResult()); ASSERT_EQ(game.getActivePlayer().isMoving(), true); ASSERT_EQ(game.getActivePlayer().getPosition(), game.getTotalRollResult()); }
第二步:补全游戏逻辑的边界处理
在checkForDoubles()函数中添加对连续两次非重骰场景的处理,确保所有合法场景下的状态都能正确更新:
std::string logic::Game::checkForDoubles() { std::string message; if (m_doublesInCurrentTurn == 0 && m_throwsInCurrentTurn == 1) { m_canThrow = false; m_canMove = true; } // 添加该分支,覆盖连续两次非重骰的场景 if (m_doublesInCurrentTurn == 0 && m_throwsInCurrentTurn == 2) { m_canThrow = false; m_canMove = true; } if (m_doublesInCurrentTurn == 1 && m_throwsInCurrentTurn == 1) { message = "Doubles! Roll again."; m_canThrow = true; m_canMove = false; } if (m_doublesInCurrentTurn == 1 && m_throwsInCurrentTurn == 2) { m_canThrow = false; m_canMove = true; } if (m_doublesInCurrentTurn == 2 && m_throwsInCurrentTurn == 2) { message = "Doubles again! You are going to jail."; m_canThrow = false; m_canMove = false; getActivePlayer().lockInJail(); } return message; }
为了彻底消除测试的随机性,建议给Dice类添加固定点数的模拟能力,这样测试场景可以完全受控,不再依赖随机掷骰:
class Dice { public: void setFixedRoll(int value) { m_fixedValue = value; m_useFixed = true; } void resetToRandom() { m_useFixed = false; } int roll() { if (m_useFixed) return m_fixedValue; // 原随机掷骰逻辑 return rand() % 6 + 1; } private: int m_fixedValue = 0; bool m_useFixed = false; };
在测试中,你可以直接设置第一次掷出两个相同点数(比如都为3),第二次掷出不同点数(比如2和4),完全避免随机性带来的测试不稳定。
内容的提问来源于stack exchange,提问作者JohnDoe




