Skip to content

Commit

Permalink
Replace unit tests with acceptance tests
Browse files Browse the repository at this point in the history
I create a new library that is independent from JUnit 4. Therefore I
need tests that make it easy for me to see that I have a proper
replacements for each rule. These test must therefore reflect the tests
that are written using System Rules. The new tests do this.
  • Loading branch information
stefanbirkner committed Apr 25, 2018
1 parent 3184a6e commit 9f8ef1d
Show file tree
Hide file tree
Showing 18 changed files with 2,916 additions and 1,774 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</parent>

<artifactId>system-rules</artifactId>
<version>1.17.1</version>
<version>1.17.2-SNAPSHOT</version>
<packaging>jar</packaging>

<name>System Rules</name>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package org.junit.contrib.java.lang.system;

import org.junit.*;
import org.junit.internal.AssumptionViolatedException;
import org.junit.internal.runners.model.EachTestNotifier;
import org.junit.internal.runners.statements.RunAfters;
import org.junit.internal.runners.statements.RunBefores;
import org.junit.rules.RunRules;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.*;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;

import static java.util.Collections.emptyList;
import static java.util.Collections.singleton;

public class AcceptanceTestRunner extends BlockJUnit4ClassRunner {
private static final Collection<Failure> NO_FAILURES = emptyList();
private final Method expectFailure;
private final Method verifyStateAfterTest;
private final Method verifyResult;
private final TestClass testClass;

public AcceptanceTestRunner(Class<?> testClass) throws InitializationError {
super(extractInnerTestClass(testClass));
expectFailure = extractMethod(testClass, "expectFailure", Failure.class);
verifyResult = extractMethod(testClass, "verifyResult", Collection.class);
verifyStateAfterTest = extractMethod(testClass, "verifyStateAfterTest");
this.testClass = new TestClass(testClass);
verifyCheckPresent();
}

private static Class<?> extractInnerTestClass(Class<?> testClass)
throws InitializationError {
Class<?>[] innerClasses = testClass.getClasses();
if (innerClasses.length > 1)
throw new InitializationError("The class " + testClass
+ " has " + innerClasses.length + " inner classes, but only"
+ " one inner class with name TestClass is expected.");
else if (innerClasses.length == 0
|| !innerClasses[0].getSimpleName().equals("TestClass"))
throw new InitializationError("The class " + testClass
+ " has no inner class with name TestClass.");
else
return innerClasses[0];
}

private void verifyCheckPresent() {
boolean noCheck = expectFailure == null
&& verifyResult == null
&& verifyStateAfterTest == null;
if (noCheck)
throw new IllegalStateException(
"No expectation is defined for the test " + getName()
+ ". It needs either a method expectFailure, verifyResult or verifyStateAfterTest.");
}

private static Method extractMethod(Class<?> testClass, String name, Class<?>... parameterTypes) {
try {
return testClass.getMethod(name, parameterTypes);
} catch (NoSuchMethodException e) {
return null;
}
}

@Override
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
Description description = describeChild(method);
if (method.getAnnotation(Ignore.class) != null)
notifier.fireTestIgnored(description);
else
runTest(methodBlock(method), description, notifier);
}

protected Statement classBlock(final RunNotifier notifier) {
Statement statement = super.classBlock(notifier);
statement = withBeforeClasses(statement);
statement = withAfterClasses(statement);
statement = withClassRules(statement);
return statement;
}

protected Statement withBeforeClasses(Statement statement) {
List<FrameworkMethod> befores = testClass
.getAnnotatedMethods(BeforeClass.class);
return befores.isEmpty() ? statement :
new RunBefores(statement, befores, null);
}

protected Statement withAfterClasses(Statement statement) {
List<FrameworkMethod> afters = testClass
.getAnnotatedMethods(AfterClass.class);
return afters.isEmpty() ? statement :
new RunAfters(statement, afters, null);
}

private Statement withClassRules(Statement statement) {
List<TestRule> classRules = classRules();
return classRules.isEmpty() ? statement :
new RunRules(statement, classRules, getDescription());
}

protected List<TestRule> classRules() {
return testClass.getAnnotatedFieldValues(
null, ClassRule.class, TestRule.class);
}

private void runTest(Statement statement, Description description,
RunNotifier notifier) {
EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description);
eachNotifier.fireTestStarted();
try {
statement.evaluate();
handleNoFailure(eachNotifier);
} catch (AssumptionViolatedException e) {
handleFailedAssumption(description, eachNotifier, e);
} catch (Throwable e) {
handleException(description, eachNotifier, e);
} finally {
invokeIfPresent(verifyStateAfterTest, eachNotifier);
eachNotifier.fireTestFinished();
}
}

private void handleNoFailure(EachTestNotifier eachNotifier) {
if (expectFailure != null)
eachNotifier.addFailure(new AssertionError("Test did not fail."));
invokeIfPresent(verifyResult, eachNotifier, NO_FAILURES);
}

private void handleFailedAssumption(Description description, EachTestNotifier eachNotifier, AssumptionViolatedException e) {
eachNotifier.addFailedAssumption(e);
if (expectFailure != null)
eachNotifier.addFailure(new AssertionError("Test did not fail."));
invokeIfPresent(
verifyResult, eachNotifier, singleton(new Failure(description, e)));
}

private void handleException(Description description, EachTestNotifier eachNotifier,
Throwable e) {
invokeIfPresent(
verifyResult, eachNotifier, singleton(new Failure(description, e)));
invokeIfPresent(
expectFailure, eachNotifier, new Failure(description, e));
if (expectFailure == null && verifyResult == null)
eachNotifier.addFailure(e);
}

private void invokeIfPresent(Method method, EachTestNotifier notifier, Object... args) {
if (method != null)
try {
method.invoke(null, args);
} catch (IllegalAccessException e) {
fail(notifier, e, "Failed to invoke '" + method.getName() + "'.");
} catch (InvocationTargetException e) {
fail(notifier, e, "Failed to invoke '" + method.getName() + "'.");
} catch (Exception e) {
notifier.addFailure(e);
}
}

private void fail(EachTestNotifier eachNotifier, InvocationTargetException e, String message) {
if (e.getCause() instanceof AssertionError)
eachNotifier.addFailure(e.getCause());
else
fail(eachNotifier, (Exception) e, message);
}

private void fail(EachTestNotifier eachNotifier, Exception e, String message) {
AssertionError error = new AssertionError(message);
error.initCause(e);
eachNotifier.addFailure(error);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,79 +2,127 @@

import static java.lang.System.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.contrib.java.lang.system.Executor.executeTestWithRule;
import static org.junit.contrib.java.lang.system.Statements.SUCCESSFUL_TEST;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runners.model.Statement;
import org.junit.*;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.junit.runner.notification.Failure;

import java.util.Map;
import java.util.Properties;
import java.util.Collection;

@RunWith(Enclosed.class)
public class ClearSystemPropertiesTest {
@Rule
public final RestoreSystemProperties restore = new RestoreSystemProperties();

@Test
public void properties_are_cleared_at_start_of_test() {
setProperty("first property", "dummy value");
setProperty("second property", "another dummy value");
ClearSystemProperties rule = new ClearSystemProperties(
"first property", "second property");
TestThatCapturesProperties test = new TestThatCapturesProperties();
executeTestWithRule(test, rule);
assertThat(test.propertiesAtStart)
.doesNotContainKey("first property")
.doesNotContainKey("second property");
}
@RunWith(AcceptanceTestRunner.class)
public static class properties_are_cleared_at_start_of_test {
@ClassRule
public static final RestoreSystemProperties RESTORE
= new RestoreSystemProperties();

@BeforeClass
public static void populateProperties() {
setProperty("first property", "dummy value");
setProperty("second property", "another dummy value");
}

public static class TestClass {
@Rule
public final ClearSystemProperties clearSystemProperties
= new ClearSystemProperties("first property", "second property");

@Test
public void test() {
assertThat(getProperty("first property")).isNull();
assertThat(getProperty("second property")).isNull();
}
}

@Test
public void property_is_cleared_after_added_to_rule_within_test() {
setProperty("property", "dummy value");
ClearSystemProperties rule = new ClearSystemProperties();
TestThatAddsProperty test = new TestThatAddsProperty("property", rule);
executeTestWithRule(test, rule);
assertThat(test.propertiesAfterAddingProperty)
.doesNotContainKey("property");
public static void verifyResult(Collection<Failure> failures) {
assertThat(failures).isEmpty();
}
}

@Test
public void after_test_properties_have_the_same_values_as_before() {
setProperty("first property", "dummy value");
setProperty("second property", "another dummy value");
setProperty("third property", "another dummy value");
ClearSystemProperties rule = new ClearSystemProperties(
"first property", "second property");
executeTestWithRule(new TestThatAddsProperty("third property", rule), rule);
assertThat(getProperties())
.containsEntry("first property", "dummy value")
.containsEntry("second property", "another dummy value")
.containsEntry("third property", "another dummy value");
@RunWith(AcceptanceTestRunner.class)
public static class property_is_cleared_after_added_to_rule_within_test {
@ClassRule
public static final RestoreSystemProperties RESTORE
= new RestoreSystemProperties();

@BeforeClass
public static void populateProperty() {
setProperty("property", "dummy value");
}

public static class TestClass {
@Rule
public final ClearSystemProperties clearSystemProperties
= new ClearSystemProperties();

@Test
public void test() {
clearSystemProperties.clearProperty("property");
assertThat(getProperty("property")).isNull();
}
}

public static void verifyResult(Collection<Failure> failures) {
assertThat(failures).isEmpty();
}
}

@Test
public void property_that_is_not_present_does_not_cause_failure() {
clearProperty("property");
ClearSystemProperties rule = new ClearSystemProperties("property");
executeTestWithRule(SUCCESSFUL_TEST, rule);
//everything is fine if no exception is thrown
@RunWith(AcceptanceTestRunner.class)
public static class after_test_properties_have_the_same_values_as_before {
@ClassRule
public static final RestoreSystemProperties RESTORE
= new RestoreSystemProperties();

@BeforeClass
public static void populateProperty() {
setProperty("first property", "dummy value");
setProperty("second property", "another dummy value");
setProperty("third property", "another dummy value");
}

public static class TestClass {
@Rule
public final ClearSystemProperties clearSystemProperties
= new ClearSystemProperties("first property", "second property");

@Test
public void test() {
clearSystemProperties.clearProperty("third property");
}
}

public static void verifyStateAfterTest() {
assertThat(getProperty("first property")).isEqualTo("dummy value");
assertThat(getProperty("second property")).isEqualTo("another dummy value");
assertThat(getProperty("third property")).isEqualTo("another dummy value");
}
}

private class TestThatAddsProperty extends Statement {
private final String property;
private ClearSystemProperties rule;
Map<Object, Object> propertiesAfterAddingProperty;
@RunWith(AcceptanceTestRunner.class)
public static class property_that_is_not_present_does_not_cause_failure {
@ClassRule
public static final RestoreSystemProperties RESTORE
= new RestoreSystemProperties();

@BeforeClass
public static void ensurePropertyIsNotPresent() {
clearProperty("property");
}

public static class TestClass {
@Rule
public final ClearSystemProperties clearSystemProperties
= new ClearSystemProperties("property");

TestThatAddsProperty(String property, ClearSystemProperties rule) {
this.property = property;
this.rule = rule;
@Test
public void test() {
}
}

@Override
public void evaluate() throws Throwable {
rule.clearProperty(property);
propertiesAfterAddingProperty = new Properties(getProperties());
public static void verifyResult(Collection<Failure> failures) {
assertThat(failures).isEmpty();
}
}
}
Loading

0 comments on commit 9f8ef1d

Please sign in to comment.