首页 > 解决方案 > 如何在一组值上正确使用 assertThrows?

问题描述

我已经学习了一点Java,所以请告知在这种情况下异常抛出测试应该是什么样子?我有以下赌博机课程。然后对其进行 2 次测试。我真的不知道第二种方法(shouldThrowWhenNumbersOutOfRange)中的“整数”应该遵循什么。您能否告知确切的语法?


public class GamblingMachine {

    public int howManyWins(Set<Integer> userNumbers) throws InvalidNumbersException {
        validateNumbers(userNumbers);
        Set<Integer> computerNumbers = generateComputerNumbers();
        int count = 0;
        for (Integer number : userNumbers) {
            if (computerNumbers.contains(number)) {
                count++;
            }
        }
        return count;
    }

    private void validateNumbers(Set<Integer> numbers) throws InvalidNumbersException {
        if (numbers.size() != 6) {
            throw new InvalidNumbersException();
        }

        if (numbers.stream().anyMatch(number -> number < 1 || number > 49)) {   //anyMatch-function to check whether any element in list satisfy given condition
            throw new InvalidNumbersException();
        }
    }

    private Set<Integer> generateComputerNumbers() {
        Set<Integer> numbers = new HashSet<>();
        Random generator = new Random();
        while(numbers.size() < 6) {
            numbers.add(generator.nextInt(49) + 1);
        }
        return numbers;
    }
}

 private GamblingMachine machine = new GamblingMachine();

    @ParameterizedTest
    @NullAndEmptySource
    public void shouldThrowWhenNumbersEmpty(Set<Integer> numbers) throws InvalidNumbersException {
        Assertions.assertThrows(NumberFormatException.class, () -> {
        Integer.parseInt(" ");
        });
    }

    @ParameterizedTest
    @CsvFileSource(resources ="/numbersOutOfRange.cvs", numLinesToSkip = 1)
    public void shouldThrowWhenNumbersOutOfRange(Set<Integer> numbers) throws InvalidNumbersException {
      Assertions.assertThrows(NumberFormatException.class, () -> {
            Integer.   //how code should look like here?
        });

    }

标签: javacsvtestingassertparametrized-testing

解决方案


测试的重点是,你知道,测试一些东西。您的shouldThrowWhenNumbersEmpty测试不会那样做(好吧,它测试会Integer.parseInt(" ")抛出一些东西。它当然会。你......不必测试核心库)。

换句话说,您的赌博机测试需要从您的 GamblingMachine 类中调用一些东西。这个想法是测试 GamblingMachine。不去测试Integer.parseInt

这也是一个奇怪的测试:为什么在 blazes 中是shouldThrowWhenNumbersEmpty 参数化的?我假设该测试的重点是:“确保赌博机在传递一组空数字时按设计工作,特别是在设计中声明InvalidNumbersException如果你这样做会抛出 an 的部分”。

这是通过以下方式完成的:

@Test
public void shouldThrowWhenNumbersEmpty() {
    Assertions.assertThrows(InvalidNumbersException.class, () -> {
        Set<Integer> empty = Set.of();
        machine.howManyWins(empty);
    });
}

参数化测试是一个相当奇特的概念。您的测试设置似乎陷入了一个陷阱:您似乎设置了重复您的赌博机类中已有的所有逻辑,然后将此逻辑应用于传入(参数化)数据,找出您的赌博机应该做,然后仔细检查它的工作。

这不是您应该编写测试的方式。测试侧重于特定结果。参数化测试是有意义的,但前提是您必须为任何给定输入做的事情大致相同。例如:

善用参数化测试

你有一个包含一堆行的 csv 文件,每行都有 6 个卷 + 正确答案。您的参数化测试将每一行视为相同:howManyWins使用 6 卷作为输入调用,然后检查是否howManyWins返回预期值。

参数化测试使用不当

您有一个 csv 文件,其中包含一堆行,每行有 6 个卷。您的参数化测试将计算出正确的掷骰结果,然后调用赌博机,并检查赌博机给出的答案是否与您计算的结果相同。

这很糟糕:您只是在重复代码。这也意味着您的测试代码本身所做的不仅仅是最基本的(它正在执行一堆业务逻辑),因此提出了一个问题:那么谁来测试您的测试呢?

您的两种测试方法似乎都不应该参数化,除非 csv 也包含结果。


推荐阅读