Re: Hierarchical Context Runner for JUnit

Stefan Bechtold <[email protected]> Fri, 01 Nov 2013 09:22:31 +0100
Newsgroups gmane.comp.java.junit.user
Message-ID <[email protected]>
Hi Aaron

it's kind of funny, because I also started to extend the BlockJUnit4ClassRunner. It finally turned out not to be a good idea, as there are to many implications that require modifications on the BlockJUnit4ClassRunner and might break dependencies and, therefore, I decided to create this runner from scratch (whereas, "from scratch" here means that I copied the BlockJUnit4ClassRunner and its parent class into one class and applied a lot of refactoring until I was able to easily include the context behavior, while I constantly compared the results of the hierarchical context runner with the results of the BlockJUnit4ClassRunner on the test suite of FitNesse).

The hierarchical context runner is able to process @BeforeClass and @AfterClass methods, as well as @Rule and @Before/@After statements along the whole context hierarchy. Let's shortly talk about them each at a time:

@BeforeClass / @AfterClass: Those are supported for the top level class and as they are static, you cannot have them within non-static member classes. My runner requires all contexts to be non-static and, therefore, only the top level class will contain those annotations. Methods annotated with these are executed right before / after the whole test hierarchy, i.e. all tests within the hierarchy run between the @BeforeClass and @AfterClass methods.

@Rule: All rules are evaluated along the whole context path. It doesn't matter where you specify the rule, as long as it is visible from the current test, it will be available and evaluated.

@Before / @After: Same as for @Rule. For each test all @Before and @After methods are run, if they are part of the context path (class, superclass, super-superclass,…) of the current test.

I didn't spend much time to think about having a @RunWith on inner classes because I think this would more or less be better placed into another test class. I mean, if I choose a new runner for the inner class that "most probably" will not handle the hierarchy, why won't I simple put this inner class in a separate file? That's just a first guess…

I am really looking forward to seeing your feedback, espc. as you have also investigated a lot of time in writing such a runner. :-)

All the best

Bechte


Am 01.11.2013 um 07:04 schrieb Aaron VonderHaar <[email protected]>:

> 
> Nice work!
> 
> I did what appears to be the same thing a few months ago: https://github.com/avh4/junit-nested
> 
> I'll have to look at your code a bit more, but it looks like you wrote a new runner from scratch and re-implemented all the behaviors of BlockJUnit4ClassRunner?  In my approach, I actually subclassed BlockJUnit4ClassRunner: https://github.com/avh4/junit-nested/blob/master/src/main/java/net/avh4/test/junit/InnerRunner.java
> 
> In junit-nested, I didn't yet get the following to work:
>   - @BeforeClass methods for outer classes (though they do work for the inner-most classes)
>   - @AfterClass methods for outer classes (though they do work for the inner-most classes)
>   - @Rule *methods* for outer classes (however, the more common @Rule *fields* do work)  (and again, they do work for inner-most classes)
>   - @RunWith on inner classes (I think this would probably be a nightmare to implement)
> 
> Does junit-hierarchicalcontextrunner handle any of those?
> 
> Cheers,
> --Aaron V.
> 
> 
> On Thu, Oct 31, 2013 at 11:18 AM, Stefan Bechtold <[email protected]> wrote:
>  
> Hi folks
> 
> I investigated some time to come up with a new Runner for JUnit and I would like to get feedback and your input about it. The runner aims on test hierarchies, i.e. tests that can be grouped into contexts for better readability. You can compare this with the contexts in rspec and I would say it is an enhancement of the Enclosed Runner.
> 
> Let's start with an example:
> 
> @RunWith(HierarchicalContextRunner.class)
> public class BankAccountTest {
> private Account account;
> 
> public class SavingsAccountContext {
> @Before
> public void setup() {
> myAccount = new SavingsAccount();
> }
> 
> @Test
> public void accountWithBalanceOf100_earns3InInterest() throws Exception {
> myAccount.setBalance(100.0);
> assertThat(myAccount.getInterestEarned(), is(closeTo(3.0, 0.001)));
> }
> 
> @Test
> public void accountWithBalanceOf200_earns6InInterest() throws Exception {
> myAccount.setBalance(200.0);
> assertThat(myAccount.getInterestEarned(), is(closeTo(6.0, 0.001)));
> }
> }
> 
> public class MoneyMarketAccountContext {
> @Before
> public void setup() {
> myAccount = new MoneyMarketAccount();
> }
> 
> @Test
> public void accountWithBalanceOf100_earns4InInterest() throws Exception {
> myAccount.setBalance(100.0);
> assertThat(myAccount.getInterestEarned(), is(closeTo(4.0, 0.001)));
> }
> 
> @Test
> public void accountWithBalanceOf200_earns8InInterest() throws Exception {
> myAccount.setBalance(200.0);
> assertThat(myAccount.getInterestEarned(), is(closeTo(8.0, 0.001)));
> }
> }
> }
> 
> (This example is copied from the newest video of Robert Martin from his website http://www.cleancoders.com, video 22 - Test Process, starting at 00:32:38)
> 
> Key points to note:
> 
> 1. There are no static classes that contain tests.
> 2. There are no extends statements, hierarchies are created by creating inner classes (nice syntax).
> 3. Each context can access all variables of the parent context.
> 4. @Rule, @Before and @After annotations are run for the complete hierarchy of a test (not visible from the example)
> 5. Mostly all features of the BlockJUnit4ClassRunner are also supported.
> 
> Another example can be found on my github.org page at https://github.com/bechte/junit-hierarchicalcontextrunner/wiki.
> 
> For me, this runner closes a gap of missing ability of the JUnit framework to support hierarchies.
> 
> I am really looking forward to getting some feedback on this runner.
> 
> All the best
> 
> Bechte
> 
> -- 
> 
> Stefan Bechtold
> 
> eCard: http://ecard.bechte.de/
> 
> http://www.bechte.de, http://twitter.com/bechte 
> 
> [Non-text portions of this message have been removed]
> 
> 
> 
> 
> -- 
> --Aaron V.
> [ http://github.com/avh4 ]
> 
> 



[Non-text portions of this message have been removed]