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

如何为包含随机数的方法编写JUnit测试?

解决依赖随机数的JUnit测试问题

我懂你遇到的困扰啦——依赖随机数的函数确实不好写稳定的测试,每次跑测试结果都可能跳变,根本没法精准断言预期值。核心思路是把随机数的生成逻辑抽成可控制的依赖,让测试时能完全掌控输入,从而验证函数的逻辑正确性。下面给你具体方案和示例:

第一步:改造原代码,解耦随机数依赖

首先得把硬编码的new Random()抽出来,通过构造方法或者 setter 注入,这样测试时可以传入我们自己控制的随机数生成器。比如原来的代码可能是这样的:

public class RpsGame {
    // 原方法:直接在内部创建Random,测试时无法控制
    public String playRandomChoice() {
        Random random = new Random();
        int choiceNum = random.nextInt(3);
        return getChoiceByNum(choiceNum);
    }

    // 根据数字返回对应的手势
    public String getChoiceByNum(int num) {
        switch(num) {
            case 0: return "rock";
            case 1: return "paper";
            case 2: return "scissor";
            default: throw new IllegalArgumentException("无效数字:" + num);
        }
    }
}

我们把它改造成支持依赖注入的版本:

public class RpsGame {
    private final Random random;

    // 构造方法注入Random,生产环境用new Random(),测试用可控的实例
    public RpsGame(Random random) {
        this.random = random;
    }

    public String playRandomChoice() {
        int choiceNum = random.nextInt(3);
        return getChoiceByNum(choiceNum);
    }

    // 保留原有的getChoiceByNum方法
    public String getChoiceByNum(int num) {
        // ... 实现同上
    }
}

第二步:编写稳定的JUnit测试

现在我们有两种方式来写测试:

方式1:使用固定种子的Random

选一个特定的种子,确保nextInt(3)返回我们想要的数字,这样就能精准断言结果:

import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class RpsGameTest {
    @Test
    public void playRandomChoice_returnsRock_whenRandomGives0() {
        // 用固定种子的Random,确保第一次nextInt(3)返回0(你可以提前验证种子对应的输出)
        Random fixedRandom = new Random(456);
        RpsGame game = new RpsGame(fixedRandom);
        
        assertEquals("rock", game.playRandomChoice());
    }

    @Test
    public void playRandomChoice_returnsPaper_whenRandomGives1() {
        Random fixedRandom = new Random(789); // 这个种子要保证nextInt(3)返回1
        RpsGame game = new RpsGame(fixedRandom);
        
        assertEquals("paper", game.playRandomChoice());
    }
}

方式2:用Mockito Mock Random(更灵活)

如果不想纠结种子,直接用Mockito框架来指定nextInt(3)的返回值,完全控制输入:

import org.junit.Test;
import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;

public class RpsGameTest {
    @Test
    public void playRandomChoice_returnsScissor_whenRandomGives2() {
        // Mock一个Random实例
        Random mockRandom = Mockito.mock(Random.class);
        // 指定调用nextInt(3)时返回2
        Mockito.when(mockRandom.nextInt(3)).thenReturn(2);
        
        RpsGame game = new RpsGame(mockRandom);
        assertEquals("scissor", game.playRandomChoice());
    }
}

为什么要这么做?

我们测试的核心是验证函数的逻辑是否正确——也就是当随机数返回0/1/2时,是否能正确调用getChoiceByNum并返回对应的手势。通过解耦随机数依赖,我们把“随机”的部分变成了可控制的输入,这样测试就能稳定通过,不会因为随机波动失败。

如果是没法修改的遗留代码,你可以用PowerMock来mock静态的new Random()调用,但这种方式不推荐,最好还是优先改造代码解耦依赖,让代码更易测。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:32:15