Example Command and @Before / @After

118 views
Skip to first unread message

Andrew

unread,
Nov 22, 2015, 8:19:46 PM11/22/15
to concordion
Currently we do a number of setup/teardown actions for the entire specification using the Junit @Before and @After methods, crucially we use @Before in the specification to setup test data belonging to the specification, and @After in the base fixture to ensure this data is cleaned up, the browser is closed, etc.

Now that the @Before and @After methods are called for each example this isn't working.  I had a quick look at @BeforeClass/AfterClass but:
  • I don't believe tricks like using "this.getClass().getName()" to get the name of the test class from the base fixture will work on a static method
  • If I changed the test data store to a static then I'm concerned about this bleeding into other tests, especially if we start running tests in parallel

Any suggestions on how to work around this?

My current thought is to create two new annotations @BeforeProcesssingSpecification and @AfterProcesssingSpecification and ensure any method decorated with those get called at the appropriate time.  This would be better than @Before/After as currently if any errors are thrown in @Before then the specification is not generated (this might be a by product of using @FailFast though, I haven't looked at the issue to closely).  

Cheers
  Andrew

Tim Wright

unread,
Nov 23, 2015, 12:29:02 AM11/23/15
to Andrew, concordion

Hi Andrew,

TLDR version: it might be best *not* to use the example functionality in your test cases.



I've often wondered why jUnit's BeforeClass and AfterClass annotations are static - sometimes it causes issues. I believe that the rationale is that we shouldn't be sharing data between tests because it risks creating ordering effects - it's better to recreate all test data for each test case (or each "example" as we call them in concordion).

In fact, Martin Fowler (one of the jUnit authors) writes about this exact point:


I've also done some research and discovered that jUnit creates a new test object for *each* test case. It used to be that a concordion test had exactly one test case - now we have one per example. So a non-static before spec annotation might not work for you. Note that I have not observed this in my own jUnit usage so the jUnit behaviour might have changed over time.


eg: apparently the following code prints "0 0 0":

public class TestJunit
{

    int count = 0;

    @Test
    public void testInc1(){
        System.out.println(count++);
    }

    @Test
    public void testInc2(){
        System.out.println(count++);
    }

    @Test
    public void testInc3(){
        System.out.println(count++);
    }
}


However, pragmatically :)

  • I do like the idea of beforeProcessingSpecification and afterProcessingSpecification annotations. These are like Spring's @PostConstruct and @PreDestroy annotations. We could even have concordion:example="beforeProcessingSpecification" tags in the spec - like concordion:example="before" tags. However, I've looked at the code (including our junit integration) and it might be tricky to do cleanly.

  • You are right that this.getClass().getName() will *not* work the same static methods as it does in non-static methods. You can get around this by having the static methods take a class parameter. But that won't work for @BeforeClass methods where you can't pass parameters. Sigh.

  • One way to get around the @before annotation working differently is to create lazy initialisers. That way the initializers are only invoked once. Something like the following - but I can't figure out a good @AfterClass version.

public synchronized void before() {
  if (!initialized) {
    initialised=  true;
    ... do whatever
  }


  • If you only have one example (ie: don't use the concordion:example tag) then @Before and @After will work as they did before :)

Hope that helps,

Tim

--
You received this message because you are subscribed to the Google Groups "concordion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to concordion+...@googlegroups.com.
To post to this group, send email to conco...@googlegroups.com.
Visit this group at http://groups.google.com/group/concordion.
To view this discussion on the web, visit https://groups.google.com/d/msgid/concordion/a18c01ce-0533-4aed-a05e-b6223e724c86%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--

Tim Wright

unread,
Nov 23, 2015, 12:41:33 AM11/23/15
to Andrew, concordion

Hi all,

Just to confirm: junit does use a new fixture object for each test (or example). The following test prints "1 1". I am currently checking that the same behaviour occurs in a concordion spec using examples.

import org.junit.Test;

import java.util.concurrent.atomic.AtomicInteger;

/**
* Created by tim on 23/11/15.
*/
public class TestIndependance {

private AtomicInteger i = new AtomicInteger(0);

@Test
public void testOne() {
System.err.println(i.addAndGet(1));
}

@Test
public void testTwo() {
System.err.println(i.addAndGet(1));
}

}

Tim Wright

unread,
Nov 23, 2015, 12:49:43 AM11/23/15
to Andrew, concordion

Hey,

Just a note - concordion specifications using the example command do *not* create a new fixture object for each example. For some reason we are bypassing the junit fixture object creation code. Not sure if this is a bug or not  - I'll confer with the concordion-dev list.

Tim

Nigel Charman

unread,
Nov 23, 2015, 5:04:28 AM11/23/15
to conco...@googlegroups.com
To add to Tim's pragmatic ideas:
  • You could use ThreadLocal to allow static state that is initialised in @BeforeClass to be scoped per thread. This would then allow any shared static data to be thread-safe when running specifications in parallel.
  • I also like the idea of @BeforeSpecification and @AfterSpecification annotations (and possibly also @BeforeSuite and @AfterSuite for the whole Concordion suite). This might be something you could try out as an extension and then report back on it?
Nigel.


On 23/11/15 18:29, Tim Wright wrote:
Reply all
Reply to author
Forward
0 new messages