Compare commits

..
4 Commits
Author SHA1 Message Date
KKlochko 79c2f159e1 Add the multi-block tests support.
continuous-integration/drone/tag Build is passing
2023-10-15 12:02:02 +03:00
KKlochko b99f90132a Add the main entry point.
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-10-10 21:43:19 +03:00
KKlochko 3d6b7b4d58 Add a constructor to set only a formatter for CLIReporter.
continuous-integration/drone/tag Build is passing
2023-10-10 21:41:43 +03:00
KKlochko d1399dac02 Add the CLITestRunner to run tests and to report the result.
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-10-10 21:38:47 +03:00
4 changed files with 164 additions and 17 deletions
@@ -0,0 +1,24 @@
package space.kklochko.simple_jbdd;
import space.kklochko.simple_jbdd.tests.factories.AbstractTestRunnerFactory;
import space.kklochko.simple_jbdd.tests.factories.SimpleTestRunnerFactory;
import space.kklochko.simple_jbdd.tests.loaders.NameClassLoader;
import space.kklochko.simple_jbdd.tests.reports.AbstractReporter;
import space.kklochko.simple_jbdd.tests.reports.CLIReporter;
import space.kklochko.simple_jbdd.tests.reports.fmt.SimpleTestReportFormatter;
import space.kklochko.simple_jbdd.tests.runners.CLITestRunner;
public class Main {
public static void main(String[] args) {
NameClassLoader loader = new NameClassLoader();
AbstractTestRunnerFactory testRunnerFactory = new SimpleTestRunnerFactory();
AbstractReporter reporter = new CLIReporter(new SimpleTestReportFormatter());
CLITestRunner runner = new CLITestRunner(loader, testRunnerFactory, reporter);
boolean status = runner.run();
System.exit(status ? 0 : 1);
}
}
@@ -11,37 +11,48 @@ import space.kklochko.simple_jbdd.tests.commands.decorators.BlockDecorator;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map; import java.util.*;
import java.util.TreeMap;
public class TestCommandFactory { public class TestCommandFactory {
public AbstractTestCommand create(Test input) { public AbstractTestCommand create(Test input) {
Class<?> aClass = input.getClass(); Class<?> aClass = input.getClass();
Map<String, Method> test_methods = this.getTestMethods(aClass); Map<String, ArrayList<Method>> test_methods = this.getTestMethods(aClass);
AbstractTestCommand<Test> aTestCommand = createTest(aClass, input); AbstractTestCommand<Test> aTestCommand = createTest(aClass, input);
if(test_methods.containsKey("Then")) { if(test_methods.containsKey("Then")) {
Method then = test_methods.get("Then"); ArrayList<Method> thens = getMethodsInReverseOrder(test_methods, "Then");
for(Method then : thens) {
String label = then.getAnnotation(Then.class).value(); String label = then.getAnnotation(Then.class).value();
aTestCommand = new BlockDecorator<Test>(label, aTestCommand, then, "Then"); aTestCommand = new BlockDecorator<Test>(label, aTestCommand, then, "Then");
} }
}
if(test_methods.containsKey("When")) { if(test_methods.containsKey("When")) {
Method when = test_methods.get("When"); ArrayList<Method> whens = getMethodsInReverseOrder(test_methods, "When");
for(Method when : whens) {
String label = when.getAnnotation(When.class).value(); String label = when.getAnnotation(When.class).value();
aTestCommand = new BlockDecorator<Test>(label, aTestCommand, when, "When"); aTestCommand = new BlockDecorator<Test>(label, aTestCommand, when, "When");
} }
}
if(test_methods.containsKey("Given")) { if(test_methods.containsKey("Given")) {
Method when = test_methods.get("Given"); ArrayList<Method> givens = getMethodsInReverseOrder(test_methods, "Given");
String label = when.getAnnotation(Given.class).value(); for(Method given : givens) {
aTestCommand = new BlockDecorator<Test>(label, aTestCommand, when, "Given"); String label = given.getAnnotation(Given.class).value();
aTestCommand = new BlockDecorator<Test>(label, aTestCommand, given, "Given");
}
} }
return aTestCommand; return aTestCommand;
} }
private ArrayList<Method> getMethodsInReverseOrder(Map<String, ArrayList<Method>> test_methods, String key) {
ArrayList<Method> methods = test_methods.get(key);
Collections.reverse(methods);
return methods;
}
protected AbstractTestCommand<Test> createTest(Class<?> aClass, Test input) { protected AbstractTestCommand<Test> createTest(Class<?> aClass, Test input) {
Title aTitle = aClass.getAnnotation(Title.class); Title aTitle = aClass.getAnnotation(Title.class);
String title = aClass.getName(); String title = aClass.getName();
@@ -52,8 +63,8 @@ public class TestCommandFactory {
return new SimpleTestCommand<Test>(title, input); return new SimpleTestCommand<Test>(title, input);
} }
protected <T extends Test> Map<String, Method> getTestMethods(Class<?> aClass) { protected <T extends Test> Map<String, ArrayList<Method>> getTestMethods(Class<?> aClass) {
Map<String, Method> test_methods = new TreeMap<String, Method>(); Map<String, ArrayList<Method>> test_methods = new TreeMap<>();
for(Method method : aClass.getDeclaredMethods()) { for(Method method : aClass.getDeclaredMethods()) {
Annotation[] annotations = method.getAnnotations(); Annotation[] annotations = method.getAnnotations();
@@ -62,15 +73,21 @@ public class TestCommandFactory {
continue; continue;
if(method.isAnnotationPresent(Given.class)) if(method.isAnnotationPresent(Given.class))
test_methods.put("Given", method); addMethodTo(test_methods, "Given", method);
if(method.isAnnotationPresent(When.class)) if(method.isAnnotationPresent(When.class))
test_methods.put("When", method); addMethodTo(test_methods, "When", method);
if(method.isAnnotationPresent(Then.class)) if(method.isAnnotationPresent(Then.class))
test_methods.put("Then", method); addMethodTo(test_methods, "Then", method);
} }
return test_methods; return test_methods;
} }
private void addMethodTo(Map<String, ArrayList<Method>> test_methods, String key, Method method) {
ArrayList<Method> methods = test_methods.getOrDefault(key, new ArrayList<>());
methods.add(method);
test_methods.put(key, methods);
}
} }
@@ -10,6 +10,10 @@ import org.apache.commons.lang3.StringUtils;
public class CLIReporter extends AbstractReporter { public class CLIReporter extends AbstractReporter {
final int terminalWidth = 80; final int terminalWidth = 80;
public CLIReporter(AbstractTestReportFormatter formatter) {
super(null, formatter);
}
public CLIReporter(ArrayList<AbstractMap.SimpleEntry<AbstractTestCommandMetaDataFactory, Boolean>> metadata, AbstractTestReportFormatter formatter) { public CLIReporter(ArrayList<AbstractMap.SimpleEntry<AbstractTestCommandMetaDataFactory, Boolean>> metadata, AbstractTestReportFormatter formatter) {
super(metadata, formatter); super(metadata, formatter);
} }
@@ -0,0 +1,102 @@
package space.kklochko.simple_jbdd.tests.runners;
import space.kklochko.simple_jbdd.tests.Test;
import space.kklochko.simple_jbdd.tests.commands.AbstractTestCommand;
import space.kklochko.simple_jbdd.tests.factories.AbstractTestRunnerFactory;
import space.kklochko.simple_jbdd.tests.factories.TestCommandQueueFactory;
import space.kklochko.simple_jbdd.tests.factories.meta.AbstractTestCommandMetaDataFactory;
import space.kklochko.simple_jbdd.tests.factories.meta.TestCommandMetaDataFactory;
import space.kklochko.simple_jbdd.tests.loaders.AbstractNameClassLoader;
import space.kklochko.simple_jbdd.tests.loaders.SingleTestLoader;
import space.kklochko.simple_jbdd.tests.reports.AbstractReporter;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class CLITestRunner extends AbstractTestRunner {
private ArrayList<AbstractTestCommand<Test>> commands;
private AbstractNameClassLoader classLoader;
private AbstractTestRunnerFactory runnerFactory;
private AbstractReporter reporter;
public CLITestRunner(AbstractNameClassLoader classLoader, AbstractTestRunnerFactory runnerFactory, AbstractReporter reporter) {
setClassLoader(classLoader);
setRunnerFactory(runnerFactory);
setReporter(reporter);
}
public boolean run() {
commands = createCommands(load());
ArrayList<Boolean> testStatuses = runTests(commands);
report(createMetadata(commands, testStatuses));
return isFailed(testStatuses);
}
public ArrayList<Test> load() {
Set<Class<?>> classes = this.classLoader.load();
ArrayList<Test> tests = new ArrayList();
for(var aClass : classes) {
tests.add((new SingleTestLoader(aClass.getName())).load());
}
return tests;
}
public ArrayList<AbstractTestCommand<Test>> createCommands(ArrayList<Test> tests) {
TestCommandQueueFactory factory = new TestCommandQueueFactory(tests);
return factory.create();
}
public ArrayList<Boolean> runTests(ArrayList<AbstractTestCommand<Test>> commands) {
return commands.stream()
.map(c -> runnerFactory.create(c).run())
.collect(Collectors.toCollection(ArrayList::new));
}
public ArrayList<AbstractMap.SimpleEntry<AbstractTestCommandMetaDataFactory, Boolean>> createMetadata(ArrayList<AbstractTestCommand<Test>> commands, ArrayList<Boolean> testStatuses) {
return IntStream.range(0, commands.size())
.mapToObj(i -> new AbstractMap.SimpleEntry<AbstractTestCommandMetaDataFactory, Boolean>(
new TestCommandMetaDataFactory(commands.get(i)),
testStatuses.get(i)))
.collect(Collectors.toCollection(ArrayList::new));
}
public void report(ArrayList<AbstractMap.SimpleEntry<AbstractTestCommandMetaDataFactory, Boolean>> metadata) {
reporter.setMetadata(metadata);
reporter.report();
}
public boolean isFailed(ArrayList<Boolean> testStatuses) {
return !testStatuses.stream().anyMatch(b -> b == false);
}
public AbstractNameClassLoader getClassLoader() {
return classLoader;
}
public void setClassLoader(AbstractNameClassLoader classLoader) {
this.classLoader = classLoader;
}
public AbstractTestRunnerFactory getRunnerFactory() {
return runnerFactory;
}
public void setRunnerFactory(AbstractTestRunnerFactory runnerFactory) {
this.runnerFactory = runnerFactory;
}
public AbstractReporter getReporter() {
return reporter;
}
public void setReporter(AbstractReporter reporter) {
this.reporter = reporter;
}
}