【问题标题】:How do I run the same JUnit test multiple times with different test data each time?如何每次使用不同的测试数据多次运行相同的 JUnit 测试?
【发布时间】:2014-02-19 00:48:21
【问题描述】:

我刚刚开始进行单元测试。我从教程点网站的 pdf 中完成了 junit 教程。所以我的问题是,我想测试我的调车场算法和我的 RPNEvaluator。

构造函数(以及任何其他帮助您了解上下文的变量)如下所示:

ShuntingYard.java:

private ArrayList<String> tokens = new ArrayList<String>();
public ShuntingYard(ArrayList<String> tokens) {
    this.tokens = tokens;
}

RPNEvaluator.java:

private Queue<String> polishExpression;
public RPNEvaluator(Queue<String> exp) {
    polishExpression = exp;
}

ShuntingYard.java 有一个名为 toRpn() 的方法,它将接受一个 ArrayList 并在经过一些处理后返回一个 Queue。

RPNEvaluator 有一个名为 evaluate 的方法,该方法将采用 Queue 类型并在经过一些处理后返回一个 double。

我正在尝试使用 Junit 编写一些单元测试,我想知道这个开始是否是最好的方法:

package testSuite;

import static org.junit.Assert.fail;

import java.util.ArrayList;

import org.junit.Before;
import org.junit.Test;

public class ExpressionEvaluationTest {

    /**
     * Initialise the lists to be used
     */
    @Before
    public void beforeTest() {
        ArrayList<String> exprOne = new ArrayList<String>();
        exprOne.add("3");
        exprOne.add("+");
        exprOne.add("4");
        exprOne.add("*");
        exprOne.add("2");
        exprOne.add("/");
        exprOne.add("(");
        exprOne.add("1");
        exprOne.add("-");
        exprOne.add("5");
        exprOne.add(")");
        exprOne.add("^");
        exprOne.add("2");
        exprOne.add("^");
        exprOne.add("3");

        ArrayList<String> exprTwo = new ArrayList<String>();
        exprTwo.add("80");
        exprTwo.add("+");
        exprTwo.add("2");

        ArrayList<String> exprThree = new ArrayList<String>();
        exprThree.add("2");
        exprThree.add("/");
        exprThree.add("1");
        exprThree.add("*");
        exprThree.add("4");

        ArrayList<String> exprFour = new ArrayList<String>();
        exprFour.add("11");
        exprFour.add("-");
        exprFour.add("(");
        exprFour.add("2");
        exprFour.add("*");
        exprFour.add("4");
        exprFour.add(")");

        ArrayList<String> exprFive = new ArrayList<String>();
        exprFive.add("120");
        exprFive.add("/");
        exprFive.add("(");
        exprFive.add("10");
        exprFive.add("*");
        exprFive.add("4");
        exprFive.add(")");

        ArrayList<String> exprSix = new ArrayList<String>();
        exprSix.add("600");
        exprSix.add("*");
        exprSix.add("2");
        exprSix.add("+");
        exprSix.add("20");
        exprSix.add("/");
        exprSix.add("4");
        exprSix.add("*");
        exprSix.add("(");
        exprSix.add("5");
        exprSix.add("-");
        exprSix.add("3");
        exprSix.add(")");

    }

    @Test
    public void test() {

    }

}

我打算把它放在 before() 方法中: ShuntingYard sy = new ShuntingYard(/arraylist here/);

然后在测试中,将列表传递给算法。我的问题是,我认为我要走很长的路,有一个参数化注释并将这些列表作为参数列表传递会更好吗?

还有一个问题:如果任何 ArrayLists 的测试通过,那么我确信我可以对 RPNEvaluator 评估方法执行后续测试。我希望我没有模棱两可。

非常感谢您的帮助。

【问题讨论】:

  • 注意:您可以缩短列表初始化 ArrayList exprThree = new ArrayList(Arrays.asList("2","/","1","*","4" ));
  • 谢谢,我把它们写了很长,这样我就可以练习如何用 vim 拉动、粘贴和导航,以适应重复的控件。
  • 我认为您的问题是,“每次使用不同的测试数据多次运行 JUnit 测试的最佳方法是什么?”。对吗?
  • @MikeB 是的,这基本上是我的问题。
  • @BoristheSpider 是的,这就是我想走的路。

标签: java unit-testing junit


【解决方案1】:

我会有所不同。而不是仅仅创建几组测试数据并每次调用相同的测试将其分解成有意义的东西。不要编写一个名为test() 的测试,而是为ShuntingYard 的每个方面编写几个单独的测试。例如:

@Test public void 
itDoesntDivideByZero()
{
    ArrayList<String> divideByZeroExpression = Arrays.asList("5", "0", "/");
    // Add code to call your method with this data here
    // Add code to verify your results here
}

@Test public void 
itCanAdd()
{
    ArrayList<String> simpleAdditionExpression = Arrays.asList("1", "2", "+");
    // Add code to call your method with this data here
    // Add code to verify your results here
}

等等。这将使您的 JUnit 输出更易于阅读。当失败时,您知道它在尝试添加时失败,或者在尝试评估会导致除以零的表达式时失败,等等。按照原始方式进行操作,您只会知道它test() 方法失败。

这里的每个测试都做了 3 件事:

  1. 排列测试数据
  2. 使用该数据执行一些操作
  3. 断言操作的结果符合预期

这个Arrange, Assert, Act 成语在自动化测试中很常见。您可能还会看到它称为Given, When, Then,如“鉴于这些条件,当我调用此方法时,我应该得到此结果”。

尝试摆脱编写一个测试来测试整个类或方法的心态。编写一个测试来测试方法的一部分。考虑这个类:

public class Adder {
    public int addOneTo(int someNumber) {
        return someNumber + 1;
    }
}

您最终可能会得到一个如下所示的测试套件:

@Test public void
itAddsOne()
{
    int numberToAddTo = 1;
    int result = new Adder().addOneTo(numberToAddTo);
    assertEquals("One plus one is two", 2, result);
}

@Test(expected="NullPointerException.class") public void
itChokesOnNulls()
{
   new Adder().addOneTo((Integer)null);
}

@Test public void
itDoesntOverflow()
{
    int result = new Adder().addOneTo(Integer.MAX_VALUE);
    // do whatever here to make sure it worked correctly
}

等等。

【讨论】:

  • 是的,这是一个很好的看待它的方法,我将多次测试一些方法,但这种方法真的很好。谢谢。
  • 在不同的测试中多次测试相同的方法是完全可以的。我编辑了答案以添加更多信息。
【解决方案2】:

Mike B 的建议非常好,请尝试将您的测试思维分离到每个行为/功能的测试中。

为了让您的测试更具可读性,我可能会为接收字符串的 ShuntingYard 类编写一个静态构造函数,然后您可以编写:

ShuntingYard 加法 = ShuntingYard.createFromExpresion("2+2"); assertThat(addition.getRpn().evaluate(), is(4));

你可以再重构一点,然后以这样的方式结束:

assertThat(evaluate("2+2"), is(4))

通俗易懂,易读,另外一行代码写出更多不同场景的测试。

其他选项是编写参数化测试,例如:http://www.mkyong.com/unittest/junit-4-tutorial-6-parameterized-test/,但在我看来真的很难看。此测试通常称为“数据驱动测试”,用于测试具有不同输入值的相同代码。

对于这个数据驱动的测试,一个更好的选择是使用像 spock 这样的东西,一个用于测试的 groovy 框架,它允许你编写令人难以置信的语义测试,当然你可以用来测试 java 代码,看看这个:@ 987654322@

【讨论】:

    猜你喜欢
    • 2010-10-19
    • 2014-10-02
    • 2013-06-13
    • 1970-01-01
    • 2016-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-22
    相关资源
    最近更新 更多