You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

桌游掷骰子逻辑单元测试随机失败问题排查求助

问题根源分析

1. 测试循环的逻辑条件完全错误

你的测试循环条件while (game.getThrowsInCurrentTurn() != 2 && game.getDoublesInCurrentTurn() != 1)完全偏离了预期的测试场景。这个条件的意思是:只有当既不是第2次掷骰,又从未掷出过重骰时,才继续循环

当第一次掷骰就抛出重骰(throws=1, doubles=1)时,doubles!=1为false,整个&&条件结果为false,循环直接终止。但此时你只完成了一次掷骰,还没执行第二次掷骰操作!接下来的ASSERT_EQ(game.canThrow(), false)断言自然会失败——因为此时游戏处于“掷出重骰,可再次掷骰”的状态,m_canThrowtrue

这就是测试随机失败的核心原因:当第一次掷骰就命中重骰时,测试提前终止,没有走到第二次掷骰的流程。

2. 游戏逻辑的边界场景未覆盖

你的checkForDoubles()函数缺少对连续两次非重骰m_doublesInCurrentTurn == 0 && m_throwsInCurrentTurn == 2)场景的处理。虽然这不是当前测试的目标,但如果测试流程意外进入这个场景,m_canThrowm_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

火山引擎 最新活动