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

如何为从集合随机选值的函数编写稳定的自动化测试?

解决随机选择函数的自动化测试稳定性问题

我有一个从数组中随机选择值的函数:

export function selectMovieUnderDuration(options: IStreamRequest, movies: Movie[], prevMovies: Movie[], duration: number): Movie {
    let filteredMovies: Movie[] = movies.filter(movie =>
        movie.Tags.some(tag => options.Tags.includes(tag)) &&
        movie.DurationLimit <= duration
    );

    let notRepeatMovies: Movie[] = filteredMovies.filter((item) => !prevMovies.some((obj) => obj.LoadTitle === item.LoadTitle));

    let selectedMovie: Movie = notRepeatMovies.length > 0 ?
        notRepeatMovies[Math.floor(Math.random() * notRepeatMovies.length)] :
        filteredMovies[Math.floor(Math.random() * filteredMovies.length)];

    return selectedMovie;
}

已基于Mocha/Chai编写基础测试用例,但在测试「所有符合条件的电影都已在prevMovies中,函数需从过滤后的列表随机选择」的场景时,当前测试仅会偶尔失败(比如函数错误返回不符合条件的电影)。希望让测试能稳定通过或失败,且不想对原函数做过度重构。


方法1:Mock随机数,固定返回结果

通过替换Math.random的实现,让随机选择的结果完全可预测,从而稳定验证逻辑正确性。

测试代码示例:

it('should return valid movie from filtered list when all are in prevMovies', () => {
    // 保存原生Math.random,避免影响其他测试
    const originalRandom = Math.random;
    // 固定返回0,确保每次选中过滤列表的第一个元素
    Math.random = () => 0;

    try {
        const movies: Movie[] = [inception, matrix, interstellar, dune];
        const prevMovies: Movie[] = [...movies];
        const args: ContStreamRequest = new ContStreamRequest('password', 'title', 'env', [], ['Sci-Fi', 'Action']);

        const result = proEng.selectMovieUnderDuration(args, movies, prevMovies, 9000);
        // 此时必然返回filteredMovies的第一个元素inception
        expect(result).to.equal(inception);
    } finally {
        // 恢复原生随机数方法
        Math.random = originalRandom;
    }
});

若要验证随机逻辑覆盖所有可能,可多次替换Math.random的返回值(比如0.5、0.99),分别测试选中对应位置的元素是否符合预期。

方法2:多次执行测试,验证所有结果合规

既然函数是随机逻辑,就通过多次调用它,确保每次返回的结果都在允许的范围内。只要有一次返回错误值,测试就会稳定失败。

测试代码示例:

it('should always return valid movie from filtered list when all are in prevMovies', () => {
    const movies: Movie[] = [inception, matrix, interstellar, dune];
    const prevMovies: Movie[] = [...movies];
    const args: ContStreamRequest = new ContStreamRequest('password', 'title', 'env', [], ['Sci-Fi', 'Action']);
    const validMovies = [inception, matrix];

    // 执行100次,足够覆盖随机场景的可能性
    for (let i = 0; i < 100; i++) {
        const result = proEng.selectMovieUnderDuration(args, movies, prevMovies, 9000);
        expect(result).to.be.oneOf(validMovies);
    }
});

这种方式无需修改原函数或Mock,通过多次执行降低随机带来的测试不稳定问题。

方法3:拆分逻辑单独测试(可选,非过度细粒度)

虽然函数看似简单,但拆分过滤与选择逻辑后,测试会更清晰且稳定:

  1. 测试filteredMovies过滤逻辑:验证仅保留符合标签和时长要求的电影
  2. 测试notRepeatMovies过滤逻辑:验证排除了prevMovies中的电影
  3. 测试选择逻辑:当notRepeatMovies为空时,从filteredMovies中随机选择

拆分后的测试示例(以过滤逻辑为例):

it('should filter movies correctly by tags and duration', () => {
    const movies: Movie[] = [inception, matrix, interstellar, dune];
    const args: ContStreamRequest = new ContStreamRequest('password', 'title', 'env', [], ['Sci-Fi', 'Action']);
    const duration = 9000;

    // 直接复用原函数的过滤逻辑(若原函数不暴露,可将过滤逻辑抽为独立函数)
    const filteredMovies = movies.filter(movie =>
        movie.Tags.some(tag => args.Tags.includes(tag)) &&
        movie.DurationLimit <= duration
    );

    expect(filteredMovies).to.deep.equal([inception, matrix]);
});

这种方式让每个测试仅关注单一逻辑,稳定性更高,也不算过度细粒度,反而提升了测试的可维护性。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 12:35:01