4

I want to do a batch test like the following, however if there is an error, it does not tell me which value of i the error occurs in.

@Test
public void simpleMovingAverageBatch() {
    for (int i = 1; i < 61; ++i) {
        simpleMovingAverage(i);
    }
}

public void simpleMovingAverage(int days) {
    double sma = -1;
    int size = stock.getCloses().size();

    stock.calcSMA(days); // <- code that I'm testing

    // first days should be -1
    for (int index = size - 1; index >= size - days + 1; --index) {
        assertEquals(-1, stock.getSMA(days, index), 0.001);
    }

    for (int index = size - days - 1; index >= 0; --index) {
        sma = 0.0;
        for (int i = 0; i < days; ++i) {
            sma += stock.getCloses().get(index + i);
        }
        sma /= days;
        assertEquals(sma, stock.getSMA(days, index), 0.001);
    }
}

This is an example of what happens if I run the code as is:

Not what I want!

This is an example of what I want to have happen:

Something like this!

I've taken a look at Parameterized Tests in this tutorial, however I do not know if this the route I want to take.

Any tips?


I don't think this question is a duplicate of these:

Running the same JUnit test case multiple time with different data

Java unit test for different input data

Community
  • 1
  • 1
budi
  • 6,351
  • 10
  • 55
  • 80
  • How is your question different from the first one you linked to? – Jeffrey Jul 28 '15 at 22:05
  • Why you just don`t use assertEquals with message in front of it? It won`t produce the picture you want but will tell you which 'i' produced the error. For example: assertEquals("This is i:" + i ,-1, stock.getSMA(days, index), 0.001); – ap0calypt1c Jul 28 '15 at 22:06
  • possible duplicate of [don't let test stop on failure](http://stackoverflow.com/questions/30729112/dont-let-test-stop-on-failure) – kryger Jul 29 '15 at 08:44

3 Answers3

3

The right way to do this test is to use a parameterized test as you describe in your question. However, you said you don't want to do that. So, instead, you can always use the String message argument for your failure message.

You appear to be using assertEquals(double, expected, double actual, double delta) in this line:

assertEquals(-1, stock.getSMA(days, index), 0.001);

Instead, you could use assertEquals(String message, double expected, double actual, double delta) to give yourself a more informative error message, e.g.

assertEquals(String.format("For index %d,", index), -1, stock.getSMA(days, index), 0.001);

This will give you an error message like:

java.lang.AssertionError: For index 15, expected:<-1.0> but was:<0.0>
durron597
  • 31,968
  • 17
  • 99
  • 158
  • Downvoter, I'm sure you're long gone by now, but why? – durron597 Jul 28 '15 at 22:13
  • This looks like a good solution, however what if I want to have something similar to the second image? Will I have to use parameterized tests? – budi Jul 28 '15 at 22:19
  • @budi Yes, you will have to use parameterized tests. – durron597 Jul 28 '15 at 22:20
  • An alternative to parameters is theories: http://junit.org/apidocs/org/junit/experimental/theories/Theories.html. – Andreas Jul 28 '15 at 22:53
  • Or, even better imho, switch to testNG and use dataproviders: http://testng.org/doc/documentation-main.html#parameters-dataproviders – Andreas Jul 28 '15 at 22:55
2

Durron's answer leads to parameterized JUnit tests via @Parameters. This is a great answer and fits the question perfectly. However, I've been down that road and found it created complicated and hard to read tests.

Theories are an alternative within JUnit: junit.org/apidocs/org/junit/experimental/theories/Theories.html

If you are willing to stray from JUnit, testNG's dataproviders are quite versatile: testng.org/doc/documentation-main.html#parameters-dataproviders

Andreas
  • 4,937
  • 2
  • 25
  • 35
2

Try JUnitParams. Your test will look like:

@RunWith(JUnitParamsRunner.class)
public class ExampleTest {

    // this method provides parameters for simpleMovingAverage
    public static Iterable<Integer> parametersForSimpleMovingAverage() {
        return IntStream.rangeClosed(1, 61).boxed().collect(Collectors.toList());
    }

    @Parameters
    @Test
    public void simpleMovingAverage(int days) {
        double sma = -1;
        int size = stock.getCloses().size();

        stock.calcSMA(days); // <- code that I'm testing

        // first days should be -1
        for (int index = size - 1; index >= size - days + 1; --index) {
            assertEquals(-1, stock.getSMA(days, index), 0.001);
        }

        for (int index = size - days - 1; index >= 0; --index) {
            sma = 0.0;
            for (int i = 0; i < days; ++i) {
                sma += stock.getCloses().get(index + i);
            }
            sma /= days;
            assertEquals(sma, stock.getSMA(days, index), 0.001);
        }
    }

And when you run the test, you will have a report like you wanted to have. Even better, unlike standard JUnit Parametrized runner JUnitPramsRunner report will contain not only test case index, but the parameter for each case (sorry, I have not enough reputation to post a screenshot).

Note: you will need to add a dependency:

  • Gradle:

    testCompile 'pl.pragmatists:JUnitParams:1.0.2'
    
  • Maven:

    <dependency>
      <groupId>pl.pragmatists</groupId>
      <artifactId>JUnitParams</artifactId>
      <version>1.0.4</version>
      <scope>test</scope>
    </dependency>
    
Alex Borysov
  • 281
  • 1
  • 4