The RapidPM Blog

We are coding Java, because it is a great pleasure for us.

Is your JUnit KnowHow up to date? - Part 3

This time i want to cover the topic of parameterizing JUnit tests with the Parameterized runner which is also part of my blog series Is your JUnit knowhow up to date? The last article can be found here. So let us get started.

For this article assume that we have a simple calculator which looks as follows:
public class Calculator {
    public static int add(int x, int y) {
        return x + y;
    }
}
and want to test the add method with several different combinations of parameters. A naive solution could look like this:
    import org.junit.Test;

    import static de.bischinger.junit.parameterized.Calculator.add;
    import static org.assertj.core.api.Assertions.assertThat;

    public class NaiveBadCalculatorAddTest {

        @Test
        public void should_pass() {

            //Test parameters 1
            int x = 1;
            int y = 2;
            assertThat(add(x, y)).isEqualTo(3);

            //Test parameters 2
            x = -1;
            y = -2;
            assertThat(add(x, y)).isEqualTo(-3);

            //Test parameters 3
            x = Integer.MAX_VALUE;
            y = Integer.MAX_VALUE;
            assertThat(add(x, y)).isEqualTo(-2);

            //Test parameters 4
            Integer boxedX = Integer.valueOf(1);
            Integer boxedY = Integer.valueOf(2);
            assertThat(add(boxedX, boxedY)).isEqualTo(3);
        }
    }

To be honest...it works. But it lacks in two aspects:
  • Test isolation - which means your test method tests several different assumptions which are not separated from each other (here for example positive and negative addition as simple corner cases). That way tests are harder to understand directly in the code and also in the reports if something went wrong.
  • Clean code - the code itself is very declarative and grows badly with every new assumption you want to test.

Maybe at one point you noticed that the code could be optimized if you extract your input parameters and expected results into an array. A possible solution could look like this:
    public class OptimizedNaivBadCalculatorAddTest {

        @Test
        public void should_pass() {

            int[][] parameters = new int[][] { { 1, 2, 3 }, { -1, -2, -3 }, { Integer.MAX_VALUE, Integer.MAX_VALUE, -2 },
                { Integer.valueOf(1), Integer.valueOf(2), 3 } };

            for (int[] parameter : parameters) {
                assertThat(Calculator.add(parameter[0], parameter[1])).isEqualTo(parameter[2]);
            }
        }
    }
Now the code is more compact and readable and everything should be fine. But wait...what if you want to do that in another test case? Do you copy that construct into every test case? And what do you do if you have to do some stuff in @Before or @After with the parameters?

This is the point where JUnit provides the concept of the Parameterized runner. In fact the transposition from the code above is not that far away if you look at the following code
@RunWith(Parameterized.class)
public class NaivParameterizedCalculatorAddTest {

    @Parameters
    public static Iterable data() {
        return Arrays.asList(new Object[][] { { 1, 2, 3 }, { -1, -2, -3 }, { Integer.MAX_VALUE, Integer.MAX_VALUE, -2 },
            { Integer.valueOf(1), Integer.valueOf(2), 3 } });
    }

    private int x;
    private int y;
    private int expected;

    public NaivParameterizedCalculatorAddTest(int x, int y, int expected) {
        this.x = x;
        this.y = y;
        this.expected = expected;
    }

    @Test
    public void should_pass() {
        assertThat(Calculator.add(x,y)).isEqualTo(expected);
    }
}
As you can see the following changes have to be done:
  • Annotate your test class with @RunWith(Parameterized.class). That way JUnit knows that it has to look out for the @Parameters-Annotation
  • Provide a public static method annotated with @Parameters, a random name and a return value of either Iterable or Object[]
  • Provide a constructor with corresponding count and datatypes in which you can map the parameters to member fields
Now you can use the member fields in your test methods, have a small readable test and can use the full power of the JUnit lifecycle. But the constructor part still looks like it could be optimized with Java reflections. Therefore JUnit provides the @Parameter-Annotation as follows:
@RunWith(Parameterized.class)
public class ImprovedParameterizedCalculatorAddTest {

    @Parameters
    public static Iterable data() {
        return Arrays.asList(new Object[][] { { 1, 2, 3 }, { -1, -2, -3 }, { Integer.MAX_VALUE, Integer.MAX_VALUE, -2 },
            { Integer.valueOf(1), Integer.valueOf(2), 3 } });
    }

    @Parameter public int x;
    @Parameter(1) public int y;
    @Parameter(2) public int expected;


    @Test
    public void should_pass() {
        assertThat(Calculator.add(x,y)).isEqualTo(expected);
    }
}
So the constructor can be removed if you change the visibility of your member fields to public (thanks to reflection) and add @Parameter to every member field. JUnit just needs to know which field of the parameter array should be mapped to which member field. As you can see it works with an index number which is defaulted to 0. And again the code is more compact then before considering clean code. But what happens if some parameter combination fails within the test?

configured @Parameters
unconfigured @Parameters




I have changed the fourth test result from 1 to 3 and my IntelliJ shows me the result of the left picture. I think that is not very meaningful. But with a small detail of the @Parameters- Annotation it can look like the right picture. In order to do that you can use the name field of the @Parameters-Annotation which is defaulted to the String {index} and shows the index of the parameter field. To improve the output the following code snippet could be used to create a more meaningful output:
@Parameters(name = "{index}: Calculator.add({0},{1})={2}")


As usual all code samples can be found on my github account https://github.com/AlexBischof/junit-parameters

4 Kids: Devoxx and JavaLand

The last month was amazing. We (that is Alexander Bischof and Oliver Milke) have been on tour for JavaLand 4 Kids and for Devoxx 4 Kids. These kids are awesome and every single event we prepare for the kids makes it even more of a pleasure for us.

JavaLand 4 Kids

As you can guess from the name, the JavaLand 4 Kids took place during to the famous JavaLand conference in Phantasialand in Cologne on 2015-03-24. It was inspired by the Devoxx 4 Kids and organized alongside the rest of the conference by DOAG. It was just by chance that this event came to our attention, but we immediately agreed it would be good idea take part and use another opportunity to teach and inspire the kids.

Alexander Bischof, Arun Gupta, Oliver Milke

During dinner we sat close to Arun Gupta, who is one of the initiators and driving forces behind the Devoxx 4 Kids.

Fried Saacke, chairman of the conference team, stated that he would like to introduce JavaLand 4 Kids as integral part of the JavaLand conference and we are looking forward to next year's JavaLand 4 Kids already.

Devoxx 4 Kids

Two and a half weeks later, another Devoxx 4 Kids took place in Karlsruhe on 2015-04-11. This was the second Devoxx 4 Kids in Karlsruhe and it was mainly a chance for all the kids who missed the first event as the workshops were the same like last year. My girlfriend Anne Doege also took place as support in working with kids. She studied Social Work, which makes her a perfect addition to our team and she even helps to display to the kids that technological affinity is also for women. synyx organized a great a event for the kids and a fantastic "after work party", as well ;-)

Impressions

Events like the Devoxx 4 Kids and JavaLand 4 Kids are invaluable for the kids. They are so grateful and clever. The kids literally cling to your lips awaiting to be taught more and more.

We truly enjoy taking place in these events, since the atmosphere with the other coaches and the kids is inspiring. More importantly however, there has been some buzz around the 4-Kids events recently. The media feedback for the JavaLand was enormous, which greatly helps in spreading the idea of the Devoxx 4 Kids. Moreover, we can be proud that we had a significant participation of girls in both events. We hope this continues.

What did we do with the kids?

We introduced the kids to the Internet of Things and showed them how to work with the electronic parts of Tinkerforge. Tinkerforge provided the materials the kids played with during JavaLand 4 Kids and Devoxx 4 Kids. You can find out more about our workshop in the official Devvox 4 Kids repository and read more about it in an article about the Devoxx 4 Kids of last fall..

This is a good place to say thank you. Thank you, Tinkerforge, for providing the materials for the kids!

What's next?

Fortunately there are more events already planned. There will be a Devoxx 4 Kids in Hamburg on 2015-06-06.

There will also be another Devoxx 4 Kids in Karlsruhe this fall and hopefully a new JavaLand 4 Kids during next JavaLand. We are already looking forward to participating :)

More to read

EJB Transaction Rollback and Timers

Introduction

Today I was a little confused when a colleague of mine tried to explain to me the transaction control directives our application depends on. The tough part was to consider all the possible rollback scenarios. To get more insight into rollback behaviour, I created a simple demo project.

The project can be found on github. It demonstrates all possible cases I could think of with respect to transaction rollback and programmatic timer manipulation. I have added TODO markers in places you choose between different scenarios. I also added a comment on what is expected to happen and why.

It works with glassfish 3.2 and 4.1. Feel free to try it out and please share your thoughts. Please keep in mind, however, this project was primarily designed to run on both versions of glassfish.

The specification (JSR-318 - EJB 3.1) can found at the JCP site. I added references to specification, which relate to this document.

EJB Transaction Rollback - When does it happen?

My primitive assumption was, that if the method that spans a transaction (e. g. by declaring TransactionAttribute.REQUIRES_NEW) terminates with an exception, the associated transaction must be rolled back. However, this is not necessarily true.

Actually it depends on the type of the exception. Transactions only rollback if the exception being thrown is marked for rollback (cf. chapter 14.3.1, Table 15).

By the default, all checked exceptions are marked as rollback=false, whereas the unchecked exception are marked as rollback=true. You can explicitly specify rollback behaviour on exceptions with @ApplicationException(rollback = true / false).

If you have an EJB that uses another EJB injected via @EJB / @Inject and the invoked method executes of the same transaction (e. g. because of TransactionAttribut.REQUIRED), this method can also induce a rollback of the transaction if it throws an appropriate exception.

This means a business method may throw an exception and have the transaction committed at the same time. Conversely, by using another EJB a transaction can be rolled back even if you catch its exception. Both of these cases are shown in the demo project.

EJB Transaction Rollback and timers

Transaction rollback also influences timeout methods (i. e. methods annotated with @Schedule / @Timeout). If the transaction used for a timeout method shall be rolled back, it is required by the specification, that the method invocation will be retried at least once (cf. chapter 18.4.3). Glassfish retries the invocation 5 seconds later. However, if the second invocation induces a rollback as well, the timer will be expunged. That means you will loose all future invocation of that timer.

The specification actually disallows timeout methods to throw application exceptions (cf. chapter 18.2.5.3), however glassfish can cope with that. Timeout method invocation is retried if either the method throws an exception or the associated transaction is marked for rollback. A timeout method can also have a committed transaction even though it throws an exception. However, since this is not allowed by the spec, its behaviour is specific to the application server in use.

For glassfish, retrying timeouts and expunging timer can be configured in the admin interface (screenshot).

Programmatic manipulations of timers with the help of @Resource TimerService are also subject to transaction rollback (cf. chapter 18.2.8). Therefore registering a new @Timeout or cancelling and an existing Timer will also be reverted of the corresponding transaction is rolled back.