Last active
November 21, 2019 13:37
-
-
Save jonas-grgt/578cdfb6b4cea4c3e389a50f537b44dd to your computer and use it in GitHub Desktop.
Lightweight BDD based structurizer for Unit Tests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.function.Supplier; | |
@SuppressWarnings("unused") | |
public class Scenario<T> { | |
private State state; | |
private final String description; | |
private final Supplier<T> contextSupplier; | |
private List<Step<T>> givenSteps = new ArrayList<>(); | |
private List<Step<T>> whenSteps = new ArrayList<>(); | |
private List<Step<T>> thenSteps = new ArrayList<>(); | |
private Scenario(String description, Supplier<T> contextSupplier) { | |
this.description = description; | |
this.contextSupplier = contextSupplier; | |
} | |
public Scenario(String description) { | |
this.description = description; | |
this.contextSupplier = () -> null; | |
} | |
public static <E> Scenario<E> scenario(String description, Supplier<E> contextSupplier) { | |
return new Scenario<>(description, contextSupplier); | |
} | |
public static <E> Scenario<E> scenario(String description) { | |
return new Scenario<>(description); | |
} | |
public Scenario<T> given(String description, ContextConsumer<T> step) { | |
this.state = State.GIVEN; | |
this.state.addStepToScenario(this, description, step); | |
return this; | |
} | |
public Scenario<T> given(String description) { | |
this.state = State.GIVEN; | |
this.state.addStepToScenario(this, description, c -> { | |
}); | |
return this; | |
} | |
public Scenario<T> and(String description, ContextConsumer<T> step) { | |
this.state.addStepToScenario(this, description, step); | |
return this; | |
} | |
public Scenario<T> when(String description, Runnable step) { | |
this.state = State.WHEN; | |
this.state.addStepToScenario(this, description, step); | |
return this; | |
} | |
public Scenario<T> when(String description, ContextConsumer<T> step) { | |
this.state = State.WHEN; | |
this.state.addStepToScenario(this, description, step); | |
return this; | |
} | |
public Scenario<T> then(String description, ContextConsumer<T> step) { | |
this.state = State.THEN; | |
this.state.addStepToScenario(this, description, step); | |
return this; | |
} | |
public void verify() { | |
T context = contextSupplier.get(); | |
givenSteps.forEach(step -> { | |
try { | |
step.run(context); | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
}); | |
whenSteps.forEach(step -> { | |
try { | |
step.run(context); | |
} catch (Exception e) { | |
if(e instanceof RuntimeException) { | |
throw (RuntimeException)e; | |
} else { | |
throw new RuntimeException(e); | |
} | |
} | |
}); | |
thenSteps.forEach(step -> { | |
try { | |
step.run(context); | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
}); | |
} | |
public static interface Step<T> { | |
void run(T context) throws Exception; | |
static <T> Step<T> valueOf(String description, ContextConsumer<T> step) { | |
return new ContextAwareStep<>(description, step); | |
} | |
static <T> Step<T> valueOf(String description, Runnable step) { | |
return new ContextLessStep<>(description, step); | |
} | |
} | |
public static class ContextAwareStep<T> implements Step<T> { | |
private final String description; | |
private final ContextConsumer<T> step; | |
public ContextAwareStep(String description, ContextConsumer<T> step) { | |
this.description = description; | |
this.step = step; | |
} | |
public static <T> ContextAwareStep<T> valueOf(String description, ContextConsumer<T> runner) { | |
return new ContextAwareStep<>(description, runner); | |
} | |
public void run(T context) throws Exception { | |
step.accept(context); | |
} | |
} | |
public static class ContextLessStep<T> implements Step<T> { | |
private final String description; | |
private final Runnable step; | |
public ContextLessStep(String description, Runnable step) { | |
this.description = description; | |
this.step = step; | |
} | |
public static <T> ContextLessStep<T> valueOf(String description, Runnable runner) { | |
return new ContextLessStep<>(description, runner); | |
} | |
public void run(T context) { | |
step.run(); | |
} | |
} | |
private enum State { | |
GIVEN { | |
@Override | |
<T> void addStepToScenario(Scenario<T> scenario, String description, ContextConsumer<T> step) { | |
scenario.givenSteps.add(Step.valueOf(description, step)); | |
} | |
@Override | |
<T> void addStepToScenario(Scenario<T> scenario, String description, Runnable step) { | |
scenario.givenSteps.add(Step.valueOf(description, step)); | |
} | |
}, WHEN { | |
@Override | |
<T> void addStepToScenario(Scenario<T> scenario, String description, ContextConsumer<T> step) { | |
scenario.whenSteps.add(Step.valueOf(description, step)); | |
} | |
@Override | |
<T> void addStepToScenario(Scenario<T> scenario, String description, Runnable step) { | |
scenario.whenSteps.add(Step.valueOf(description, step)); | |
} | |
}, THEN { | |
@Override | |
<T> void addStepToScenario(Scenario<T> scenario, String description, ContextConsumer<T> step) { | |
scenario.thenSteps.add(Step.valueOf(description, step)); | |
} | |
@Override | |
<T> void addStepToScenario(Scenario<T> scenario, String description, Runnable step) { | |
scenario.thenSteps.add(Step.valueOf(description, step)); | |
} | |
}; | |
abstract <T> void addStepToScenario(Scenario<T> scenario, String description, ContextConsumer<T> step); | |
abstract <T> void addStepToScenario(Scenario<T> scenario, String description, Runnable step); | |
} | |
@FunctionalInterface | |
public interface ContextConsumer<T> { | |
void accept(T t) throws Exception; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment