如何每次使用不同的测试数据多次运行相同的JUnit测试?

时间:2014-01-27 22:54:29

标签: java unit-testing junit

我刚刚开始进行单元测试。我从教程点网站上的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评估方法执行后续测试。我希望我没有暧昧。

非常感谢帮助。

2 个答案:

答案 0 :(得分:5)

我会稍微改变一下。而不是仅创建几组测试数据并每次调用相同的测试将其分解为有意义的事物。而不是编写一个名为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()方法失败。

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

  1. 安排测试数据
  2. 使用该数据执行某些操作
  3. 断言行动结果符合预期
  4. 这种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
    }
    

    等等。

答案 1 :(得分:1)

Mike B的建议非常好,尝试按行为/功能在一次测试中分离您的测试思维。

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

ShuntingYard addition = ShuntingYard.createFromExpresion(“2 + 2”);   assertThat(addition.getRpn()。evaluate(),is(4));

你可以再重构一次,并以类似的结尾结束:

断言(评估(“2 + 2”),是(4))

这很容易理解和易于阅读,另外用不同的场景编写更多的测试它的一行代码。

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

对于这个数据驱动的测试,更好的选择是使用类似spock的东西,一个用于测试的groovy框架,允许你编写令人难以置信的语义测试,当然你可以用来测试java代码,检查一下:{ {3}}