Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Aggregating JUnit Rules with dependencies between one another

In more complex unit tests, I often require a certain set of Rules to be present. Some of these Rules have dependencies to another. As the ordering is relevant, I use RuleChains for that. All good so far.

This however is duplicated in most tests (with the occasional additional rule being used). Not only does this duplication feel unnecessary and cumbersome to repeat, it also needs to be adjusted in many places, when an additional Rule should be integrated.

What I would like to have is a Rule of Rules, i.e. a (predefined) Rule that contains or aggregates other (application & test specific) Rules.

I'll give an example of how this currently looks like:

public LoggingRule logRule = new LogRule();
public ConfigurationRule configurationRule = new ConfigurationRule();
public DatabaseConnectionRule dbRule = new DatabaseConnectionRule();
public ApplicationSpecificRule appRule = new ApplicationSpecificRule();

@Rule
RuleChain chain = RuleChain.outerRule(logRule)
                           .around(configurationRule)
                           .around(dbRule)
                           .around(appRule);

Assume that the given Rules depend on each other, e.g. the ApplicationSpecificRule requires that the DatabaseConnectionRule is executed first in order to establish a connection, the ConfigurationRule has initialized an empty configuration, etc. Also assume that for this (rather complex test) all rules are actually required.

The only solution I could come up with so far is to create factory methods that return a predefined RuleChain:

public class ApplicationSpecificRule extends ExternalResource
{
    public static RuleChain basicSet()
    {
         return RuleChain.outerRule(new LogRule())
                         .around(new ConfigurationRule())
                         .around(new DatabaseConnectionRule())
                         .around(new ApplicationSpecificRule());
    }
}

In a test this can then be used as follows:

@Rule
RuleChain chain = ApplicationSpecificRule.basicSet();

With that the duplication is removed and additional Rules can easily be integrated. One could even add test-specific Rules to that RuleChain. However one can't access the contained Rules when they are required for additional setup (assume you need the ApplicationSpecificRule in order to create some domain object, etc.).

Ideally this would be extended to also support using other predefined sets, e.g. an advandancedSet that builds on top of the basicSet of Rules.

Can this be somehow simplified? Is it a good idea in the first place or am I somehow misusing Rules? Would it help to restructure the tests? Thoughts?

like image 533
geld0r Avatar asked Mar 22 '15 14:03

geld0r


2 Answers

The TestRule interface has only one method, so it's quite easy can define your own custom rule that delegates to a RuleChain and keeps references to the other rules:

public class BasicRuleChain implements TestRule {
  private final RuleChain delegate;
  private final DatabaseConnectionRule databaseConnectionRule
      = new DatabaseConnectionRule();

  public BasicRuleChain() {
    delegate = RuleChain.outerRule(new LogRule())
        .around(new ConfigurationRule())
        .around(databaseConnectionRule)
        .around(new ApplicationSpecificRule());
  }

  @Override
  public Statement apply(Statement base, Description description) {
    return delegate.apply(base, description
  }

  public Connection getConnection() {
    return databaseConnectionRule.getConnection();
  }
}
like image 81
NamshubWriter Avatar answered Oct 19 '22 20:10

NamshubWriter


Doesn't get much simpler then that, does it? The only thing that would make it even simpler is to just use an instance instead of a factory, since you don't need fresh instances all the time.

like image 27
Jens Schauder Avatar answered Oct 19 '22 20:10

Jens Schauder