测试每次调用时可能产生不同输出的函数

时间:2017-11-16 17:48:31

标签: java testing

鉴于功能:

static boolean chance(int percentage) {
    return percentage != 0 && random.nextInt(101) <= percentage;
}

并测试该功能:

@Test
public void chance_AlwaysFalse_Percentage0() {
    assertFalse(chance(0));
}

该测试并未确定机会总是会返回true。我可以将change函数更改为以下内容:

static boolean chance(int percentage) {
    return random.nextInt(101) <= percentage;
}

并且测试仍然会通过。但是,random.nextInt(100)返回0的可能性非常小,这会使函数返回true,从而使测试失败。

我也可以执行这个测试十亿次,但考虑到随机数的性质,失败的可能性很小。

我应该如何测试这样的功能?它应该进行测试吗?有什么更好的解决这个问题的方法?

3 个答案:

答案 0 :(得分:2)

java中有一些功能可用于可重复的单元测试:

  • 随机数:带种子的Random构造函数,例如new Random(13)将始终提供相同的随机数序列。这已被利用来查找以1,2,3,...,10开头的随机序列。或某些人的字母名称。
  • 时间:新的java时间函数允许伪时钟,因此“now”始终在给定时间。 Clock.fixed
  • 然后有模拟框架,它检测任意调用的字节代码,以便返回提供的结果。对于混合更复杂的上下文也很有用。

答案 1 :(得分:0)

自动化测试无法捕获所有内容。事实上,你的函数的第一个示例也可能有一个错误,因为chance(1)将有2%的几率而不是1%的几率返回true(因为如果随机数为0或1,它将返回true。但如果你只测试0和100,那么它将在100%的时间内通过这些测试。 (实际上,0和100是唯一的值,它们将返回true正确的时间百分比;其他所有值将减少1%。该bug的影响是微妙的您可能会或可能不会注意到它,具体取决于函数的使用方式。)

如果你真的想要,你可以将你的测试放在一个循环中。如果你运行chance_AlwaysFalse_Percentage0测试10,000次,则几率非常有利于被击中的任何特定随机数。 (不,它在技术上没有保证,但如果你计算赔率,我认为你会感到满意。)我不确定这是否值得。

答案 2 :(得分:0)

软件随机数生成器不是随机的

如果用相同的种子播种,它们是确定性的。 我还在范围检查中纠正了比较逻辑是正确的。

可测试的实施

import javax.annotation.ParametersAreNonnullByDefault;
import java.util.Random;

@ParametersAreNonnullByDefault
public class Q47336122
{
    private final Random random;

    public Q47336122(final long seed)
    {
        this.random = new Random(seed);
    }

    public boolean chance(final int percentage)
    {
        if (percentage <= 0) { return false; }
        else if (percentage >= 100) { return true; }
        else
        {
            final int r = this.random.nextInt(100);
            return r > 0 && r <= percentage;
        }
    }
}

确定性测试

import org.junit.Test;

import javax.annotation.ParametersAreNonnullByDefault;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

@ParametersAreNonnullByDefault
public class Q47336122Test
{


    @Test
    public void testChance()
    {
        final Q47336122 q = new Q47336122(1000L);
        /* poor man's code generator */
//        for (int i=0; i <= 100; i++) {
//        System.out.println(String.format("assertThat(q.chance(%d),is(%s));", i, q.chance(i)));
//        }
        assertThat(q.chance(0),is(false));
        assertThat(q.chance(1),is(false));
        assertThat(q.chance(2),is(false));
        assertThat(q.chance(3),is(false));
        assertThat(q.chance(4),is(false));
        assertThat(q.chance(5),is(false));
        assertThat(q.chance(6),is(false));
        assertThat(q.chance(7),is(false));
        assertThat(q.chance(8),is(false));
        assertThat(q.chance(9),is(false));
        assertThat(q.chance(10),is(false));
        assertThat(q.chance(11),is(false));
        assertThat(q.chance(12),is(false));
        assertThat(q.chance(13),is(false));
        assertThat(q.chance(14),is(false));
        assertThat(q.chance(15),is(false));
        assertThat(q.chance(16),is(false));
        assertThat(q.chance(17),is(false));
        assertThat(q.chance(18),is(true));
        assertThat(q.chance(19),is(false));
        assertThat(q.chance(20),is(false));
        assertThat(q.chance(21),is(false));
        assertThat(q.chance(22),is(false));
        assertThat(q.chance(23),is(false));
        assertThat(q.chance(24),is(true));
        assertThat(q.chance(25),is(false));
        assertThat(q.chance(26),is(false));
        assertThat(q.chance(27),is(false));
        assertThat(q.chance(28),is(false));
        assertThat(q.chance(29),is(false));
        assertThat(q.chance(30),is(false));
        assertThat(q.chance(31),is(false));
        assertThat(q.chance(32),is(false));
        assertThat(q.chance(33),is(false));
        assertThat(q.chance(34),is(false));
        assertThat(q.chance(35),is(false));
        assertThat(q.chance(36),is(true));
        assertThat(q.chance(37),is(false));
        assertThat(q.chance(38),is(true));
        assertThat(q.chance(39),is(false));
        assertThat(q.chance(40),is(true));
        assertThat(q.chance(41),is(false));
        assertThat(q.chance(42),is(true));
        assertThat(q.chance(43),is(false));
        assertThat(q.chance(44),is(false));
        assertThat(q.chance(45),is(false));
        assertThat(q.chance(46),is(false));
        assertThat(q.chance(47),is(false));
        assertThat(q.chance(48),is(false));
        assertThat(q.chance(49),is(false));
        assertThat(q.chance(50),is(true));
        assertThat(q.chance(51),is(true));
        assertThat(q.chance(52),is(false));
        assertThat(q.chance(53),is(true));
        assertThat(q.chance(54),is(false));
        assertThat(q.chance(55),is(true));
        assertThat(q.chance(56),is(false));
        assertThat(q.chance(57),is(true));
        assertThat(q.chance(58),is(false));
        assertThat(q.chance(59),is(false));
        assertThat(q.chance(60),is(true));
        assertThat(q.chance(61),is(false));
        assertThat(q.chance(62),is(false));
        assertThat(q.chance(63),is(true));
        assertThat(q.chance(64),is(true));
        assertThat(q.chance(65),is(true));
        assertThat(q.chance(66),is(true));
        assertThat(q.chance(67),is(true));
        assertThat(q.chance(68),is(true));
        assertThat(q.chance(69),is(false));
        assertThat(q.chance(70),is(false));
        assertThat(q.chance(71),is(true));
        assertThat(q.chance(72),is(true));
        assertThat(q.chance(73),is(true));
        assertThat(q.chance(74),is(true));
        assertThat(q.chance(75),is(false));
        assertThat(q.chance(76),is(true));
        assertThat(q.chance(77),is(true));
        assertThat(q.chance(78),is(true));
        assertThat(q.chance(79),is(false));
        assertThat(q.chance(80),is(true));
        assertThat(q.chance(81),is(false));
        assertThat(q.chance(82),is(true));
        assertThat(q.chance(83),is(true));
        assertThat(q.chance(84),is(true));
        assertThat(q.chance(85),is(true));
        assertThat(q.chance(86),is(true));
        assertThat(q.chance(87),is(true));
        assertThat(q.chance(88),is(true));
        assertThat(q.chance(89),is(true));
        assertThat(q.chance(90),is(true));
        assertThat(q.chance(91),is(true));
        assertThat(q.chance(92),is(true));
        assertThat(q.chance(93),is(true));
        assertThat(q.chance(94),is(true));
        assertThat(q.chance(95),is(true));
        assertThat(q.chance(96),is(true));
        assertThat(q.chance(97),is(true));
        assertThat(q.chance(98),is(true));
        assertThat(q.chance(99),is(true));
        assertThat(q.chance(100),is(true));
    }
}

无论你运行多少次测试,它都会一直通过。

对于读者来说,扩展它以测试成千上万的输入是一项微不足道的练习。只要种子与生成测试数据时相同,它就会在测试时通过。

可注射随机数生成器实例将是一个更好的设计,并且在随机数源实际上不确定的情况下会使测试更容易。

static方法是反模式的,并且是测试问题的关键所在,这里没有任何东西无法通过一些简单的重构来解决static正如我的代码所示,并使其具有确定性和可测试性。