You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写可靠测试用例验证随机彩票数组的元素唯一性?

如何确保彩票函数的去重测试不偶然通过?

我正在编写一个彩票函数的规格说明,要求生成包含7个1到49之间随机数的数组,数组中的数字必须唯一,像[1, 2, 3, 4, 5, 6, 6]这种存在重复值的结果是无效的。

现在我有个测试用例用于检查数组数字是否唯一,但它成了“不稳定测试”——可能随机通过或失败。我目前通过多次运行测试用例来“解决”这个问题,但想找到更优雅的方案,需要一种可靠的方式验证lottery()函数能持续避免重复值。

现有测试代码

it('TC05 - lottery() each array item must be a unique number', () => {

    // function to check if an array has duplicates
    const hasDuplicates = arr => {
        return (new Set(arr)).size !== arr.length;
    }

    // run check for duplicates x times
    const REPETITIONS = 32
    let seriesOfTests = []
    for (let i = 0; i < REPETITIONS; i++) {
        if (hasDuplicates(lottery())) {
            seriesOfTests.push(true)
        } else {
            seriesOfTests.push(false)
        }
    }

    // check series to ensure each single test passed
    let seriesSummery = seriesOfTests.includes(true)

    // test case
    expect(seriesSummery).must.be.false()
})

现有彩票函数代码

// lottery returns an array consists of 7 random numbers between 1 and 49
const lottery = () => {
    let result = []

    for (let i = 0; i < 7; i++) {
        const newNbr = randomNbr(49)

        if (result.find(el => el === newNbr) === undefined) {
            result.push(newNbr)
        } else {
            i -= 1
        }
    }
    return result
}

更优雅的解决方案

1. 模拟随机数生成,强制触发重复场景

直接测试随机逻辑容易出现不稳定问题,更可靠的方式是mock掉randomNbr函数,让它返回固定的重复序列,强制彩票函数遇到重复值,验证其是否能正确处理重复情况。

以Mocha+Sinon测试框架为例:

it('TC05 - lottery() must handle duplicate numbers correctly', () => {
    // 模拟randomNbr,先返回重复值,再返回新值
    const mockRandom = sinon.stub()
                               .onCall(0).returns(5)
                               .onCall(1).returns(5) // 生成重复值
                               .onCall(2).returns(10)
                               .onCall(3).returns(15)
                               .onCall(4).returns(20)
                               .onCall(5).returns(25)
                               .onCall(6).returns(30)
                               .onCall(7).returns(35); // 第7次调用得到不重复的第7个值

    // 替换原randomNbr为mock函数
    sinon.replace(global, 'randomNbr', mockRandom);

    const result = lottery();
    // 验证结果长度为7,且包含所有预期唯一值
    expect(result.length).to.equal(7);
    expect([5,10,15,20,25,30,35].every(num => result.includes(num))).to.be.true;

    // 恢复原函数
    sinon.restore();
})

这种方式完全可控,不会出现随机波动,能100%验证函数的去重逻辑是否正确。

2. 简化现有重复测试的写法

如果不想使用mock,也可以优化现有多次运行的代码,让逻辑更直观:

it('TC05 - lottery() each array item must be a unique number', () => {
    const hasDuplicates = arr => new Set(arr).size !== arr.length;
    const REPETITIONS = 32;

    // 遍历测试,一旦发现重复立即断言失败
    for (let i = 0; i < REPETITIONS; i++) {
        const nums = lottery();
        expect(hasDuplicates(nums)).to.be.false;
    }
})

无需收集结果再统一判断,只要某次出现重复,测试会立刻失败,逻辑更简洁。

3. 优化函数实现(可选)

当前彩票函数通过生成随机数后检查重复、重复则重试的逻辑没问题,但可以优化实现方式,从根源避免重复值的产生:比如先生成1-49的完整数组,再用洗牌算法打乱顺序,最后取前7个值,这样天然保证数值唯一。

优化后的函数代码:

const lottery = () => {
    // 生成1到49的数组
    const nums = Array.from({length:49}, (_,i) => i+1);
    // Fisher-Yates洗牌算法
    for (let i = nums.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [nums[i], nums[j]] = [nums[j], nums[i]];
    }
    // 取前7个值
    return nums.slice(0,7);
}

这种实现下,只要洗牌逻辑正确,就不会出现重复值,测试只需验证数组长度、数值范围和唯一性各一次即可,无需多次运行。

内容的提问来源于stack exchange,提问作者spacePirate

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 22:47:05