views:

201

answers:

7

In Python, I often have tests which look something like this:

tests = [
    (2, 4),
    (3, 9),
    (10, 100),
]
for (input, expected_output) in tests:
    assert f(input) == expected_output

What is the "right" way to write tests like this (where a set of test cases is specified, then a loop runs each of them) in Java with JUnit?

Thanks!

Preemptive response: I realize I could do something like:

assertEquals(4, f(2))
assertEquals(9, f(3))
....

But... I'm hoping there is a better way.

A: 

Wouldn't you just define a simple class with two fields, real result and expected result and then loop over the collection in a similar way to what your Python snippet is doing?

Dana
I've considered that... I'd initially thought it was a little bit silly to create all the classes that'd be needed for that... But maybe that's just showing my inexperience with Java.
David Wolever
+9  A: 

Same thing.

    int[][] tests = {
            {2, 4},
            {3, 9},
            {10, 100}
    };
    for (int[] test : tests) {
        assertEquals(test[1], f(test[0]));
    }

Certainly not as pretty as python but few things are.

You may also want to look into JUnit Theories, a future feature...

Nick Veys
Perfect. Minor quibble: it's more Java-ish to use a List rather than an array.
slim
Most definitely, however if we're speaking to a follower-of-the-python, busting out List a = new ArrayList(); a.add(); a.add(); a.add(); their eyes will bleed!
Nick Veys
I don't see why a List is better than an array in this case.
Akbar ibrahim
Better? No. More Java-ish? Sure why not...
Nick Veys
I've been using this a bit... But it starts to break down when you have many types: what if 'f' accepts a string?
David Wolever
You'd just use a 2D String array. This scales exactly the same as the python example. Only difference is the initial type declaration, for loop syntax, {}'s and ;'s.
Nick Veys
Err, sorry, "f accepts a string and returns an int" is what I meant to say. You'd get an object array, then need to cast it later... And... Python has spoiled me :(
David Wolever
You could make a List of an inner tuple class, with one String param representing the parameter and one Integer param representing the result. It's a question of how much work you want to do in order to be correct...
Chamelaeon
A: 

There are no tuples in Java, but you could use a Map or two parallel arrays to specify input/output pairs and then do a loop just like your Python example.

Akbar ibrahim
+1  A: 

Um...

int[][] tests = new int[][]{
        {2, 4},
        {3, 9},
        {10, 100},
    };

for(int[] i : tests)
{
    assertEquals(i[1], f(i[0]);
}

Same thing, really. The only problem is Java's lack of a tuple literal, so for more complex cases, you'll have to use Object[] arrays and cast, or write a Tuple class.

Michael Borgwardt
As you mention, you've got to use Object[] arrays and casting later... Which is no fun... But it's looking like there is no "nice" way around that. Oh well.
David Wolever
+4  A: 

The right way is writing separate assert statements, even if you don't like it.

It avoids unnecessary complications, and when it fails it is sometimes easier to see which value failed (no need to start up the debugger).

However, if you generate your test data automatically it is a different story.

starblue
:( That's not very happy.What happens if you need to do a couple of lines of setup? Well you create a new method... But now each test needs two methods instead of one... And... Java makes me sad :(
David Wolever
If the setup is the same I would use the JUnit setup method. If it is different I would write a private method with the setup and assert and call it several times.
starblue
+3  A: 

Have a look at the Parameterized test runner in Junit.

http://junit.org/apidocs/org/junit/runners/Parameterized.html

It looks like it will do precisely what you are looking for.

digitaljoel
Awesome -- that looks like what I want. I'll play with it and see.
David Wolever
I played with it once, but it's been too long ago for me to give you more guidance than a link to the javadoc.
digitaljoel
A: 

Definitely not an expert on unit testing but i would prefer to have a separate method for each case that I am testing against and use some test running tools (like NUnit-GUI, for C#). That way I would exactly know which case fails, if it does. Its more work to do, but i think it eventually pays off well.

krishna
I tend to agree, however if you parameterize your failure messages you can get this information out.
Nick Veys
This becomes ridiculous very, very quickly though... Say you've got three utility functions: for each one you should be testing at least three cases (zero case, one case, many case). All of a sudden you need nine methods, each of which would no doubt duplicate code.
David Wolever