Compare commits

...
35 Commits
Author SHA1 Message Date
KKlochko 1e3446a431 Add a constructor with a default factory for TestCommandQueueFactory.
continuous-integration/drone/tag Build is passing
2023-10-08 19:56:10 +03:00
KKlochko b1ef8dc32f Fix the test title for SingleTestClassLoader.
continuous-integration/drone/tag Build is passing
2023-10-08 19:55:30 +03:00
KKlochko b57b005d97 Add the CLIReporter and its tests.
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-10-07 20:11:45 +03:00
KKlochko 9945480a6a Add the AbstractReporter. 2023-10-07 20:10:53 +03:00
KKlochko df7c7e2b9f Add default constructor for SimpleTestReportFormatter.
continuous-integration/drone/tag Build is passing
2023-10-07 20:07:31 +03:00
KKlochko 28cd218bf7 Add Apache Commons Lang library. 2023-10-07 20:05:45 +03:00
KKlochko ff09435c04 Refactor the SimpleTestReportFormatter to use the interface.
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-10-06 22:11:19 +03:00
KKlochko 6d76646d16 Add the AbstractTestReportFormatter. 2023-10-06 22:11:01 +03:00
KKlochko 458091a7e2 Fix the encoding issue.
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-10-06 18:38:54 +03:00
KKlochko 696037c2d3 Add the SimpleTestReportFormatter.
continuous-integration/drone/push Build is failing
continuous-integration/drone/tag Build is failing
2023-10-04 22:38:15 +03:00
KKlochko 189ba44c1d Add the TestCommandMetaDataFactory to generate the metadata.
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-10-03 22:33:49 +03:00
KKlochko bcdd11c5a3 Add the ClassLoader.
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-10-01 20:16:05 +03:00
KKlochko 19f638e091 Fix typos and improve description in the tests.
continuous-integration/drone/tag Build is passing
2023-10-01 20:09:07 +03:00
KKlochko 38f4cf3464 Add the NameClassLoader.
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-09-30 21:25:37 +03:00
KKlochko a0880372f8 Add the com.google.guava as a dependency. 2023-09-30 21:12:12 +03:00
KKlochko db0c3da413 Add the single class loader.
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-09-29 20:47:36 +03:00
KKlochko 75e30ddae9 Add the queue factory for test commands.
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-09-27 21:56:12 +03:00
KKlochko 9056c8d047 Add Abstract and Simple factories to create a test runner.
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-09-26 22:17:17 +03:00
KKlochko a1b85e3771 Refactor the SimpleTestRunner and its test.
continuous-integration/drone/tag Build is passing
2023-09-26 22:14:55 +03:00
KKlochko 0683be643f Refactor tests for the SimpleTestRunner.
continuous-integration/drone/push Build is passing
2023-09-25 17:09:18 +03:00
KKlochko e67bdc4bb0 Fix the BlockDecorator to throw the AsssertionError, if any.
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-09-24 21:52:25 +03:00
KKlochko 9a14ea6486 Add SimpleTestRunner as a test runner.
continuous-integration/drone/tag Build is failing
2023-09-24 21:48:49 +03:00
KKlochko 1549f85a49 Add the CI/CD configuration for Gradle and Maven build.
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-09-23 21:56:28 +03:00
KKlochko efb644e6a9 Add the create method for the TestCommandFactory. 2023-09-23 21:43:56 +03:00
KKlochko 6a3d7f4f42 Add the test block decorator. 2023-09-23 21:41:42 +03:00
KKlochko 6feb7f87dc Add abstract and simple test commands. 2023-09-23 21:34:54 +03:00
KKlochko 47af8b4522 Add a test example without a title annotation. 2023-09-23 21:31:23 +03:00
KKlochko db8fe1fc47 Update test examples to have annotations with values. 2023-09-23 21:31:01 +03:00
KKlochko e803452389 Add the Title annotation. 2023-09-23 21:25:26 +03:00
KKlochko 08c85459a7 Add the TestCommandFactory with a getTestMethods. 2023-09-20 22:23:18 +03:00
KKlochko 9edbca09cf Add the Test class as ABC. 2023-09-20 22:19:41 +03:00
KKlochko b3a010f6e9 Add test examples. 2023-09-20 20:35:30 +03:00
KKlochko b1332c838b Refactor the Sum class to be moved into the samples package. 2023-09-20 20:33:44 +03:00
KKlochko df1261f31c Add the Given-When-Then annotations. 2023-09-19 22:07:23 +03:00
KKlochko 38a6eeb889 Remove the test examples. 2023-09-19 22:01:22 +03:00
59 changed files with 1607 additions and 581 deletions
+24
View File
@@ -0,0 +1,24 @@
---
kind: pipeline
type: docker
name: gradle
steps:
- name: test
image: gradle:8.3.0-jdk17-graal
commands:
- gradle assemble
- gradle check
---
kind: pipeline
type: docker
name: maven
steps:
- name: test
image: maven:3.9.4-eclipse-temurin-17-alpine
commands:
- mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
- mvn test -B
+6
View File
@@ -12,12 +12,18 @@ java {
}
}
compileJava {
options.encoding = 'UTF-8'
}
repositories {
// Spock releases are available from Maven Central
mavenCentral()
}
dependencies {
implementation 'com.google.guava:guava:32.1.2-jre'
implementation 'org.apache.commons:commons-lang3:3.13.0'
// mandatory dependencies for using Spock
implementation platform('org.apache.groovy:groovy-bom:4.0.15')
implementation 'org.apache.groovy:groovy'
+12
View File
@@ -12,6 +12,8 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<groovy.version>4.0.15</groovy.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<build>
@@ -77,6 +79,16 @@
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.2-jre</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.13.0</version>
</dependency>
<!-- Mandatory dependencies for using Spock -->
<dependency>
<groupId>org.spockframework</groupId>
@@ -0,0 +1,13 @@
package space.kklochko.simple_jbdd.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Given {
String value() default "Given";
}
@@ -0,0 +1,13 @@
package space.kklochko.simple_jbdd.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Then {
String value() default "Then";
}
@@ -0,0 +1,13 @@
package space.kklochko.simple_jbdd.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Title {
String value() default "Title";
}
@@ -0,0 +1,13 @@
package space.kklochko.simple_jbdd.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface When {
String value() default "When";
}
@@ -1,8 +0,0 @@
package space.kklochko.simple_jbdd.test_examples;
public class Sum {
static int sum(int a, int b) {
return a + b;
}
}
@@ -0,0 +1,8 @@
package space.kklochko.simple_jbdd.test_examples.samples;
public class Sum {
public static int sum(int a, int b) {
return a + b;
}
}
@@ -0,0 +1,15 @@
package space.kklochko.simple_jbdd.test_examples.tests;
import space.kklochko.simple_jbdd.annotations.Then;
import space.kklochko.simple_jbdd.annotations.Title;
import space.kklochko.simple_jbdd.tests.Test;
@Title("A Simple Failed GivenWhenTest for Sum")
public class SimpleFailedThenTest extends Test {
@Then
public void check() {
assert 30 == 10 + 20 + 1;
}
}
@@ -0,0 +1,32 @@
package space.kklochko.simple_jbdd.test_examples.tests;
import space.kklochko.simple_jbdd.annotations.Given;
import space.kklochko.simple_jbdd.annotations.Then;
import space.kklochko.simple_jbdd.annotations.Title;
import space.kklochko.simple_jbdd.annotations.When;
import space.kklochko.simple_jbdd.test_examples.samples.Sum;
import space.kklochko.simple_jbdd.tests.Test;
@Title("A Simple GivenWhenTest for Sum")
public class SimpleGivenWhenThenTest extends Test {
private Sum sum;
private int a;
private int b;
@Given
void setup() {
sum = new Sum();
}
@When
void trigger() {
a = 10;
b = 20;
}
@Then
void check() {
assert 30 == sum.sum(a, b);
}
}
@@ -0,0 +1,14 @@
package space.kklochko.simple_jbdd.test_examples.tests;
import space.kklochko.simple_jbdd.annotations.Then;
import space.kklochko.simple_jbdd.annotations.Title;
import space.kklochko.simple_jbdd.tests.Test;
@Title("Simple test")
public class SimpleThenTest extends Test {
@Then("The sum of 10 and 20 is 30.")
public void check() {
assert 30 == 10 + 20;
}
}
@@ -0,0 +1,13 @@
package space.kklochko.simple_jbdd.test_examples.tests;
import space.kklochko.simple_jbdd.annotations.Then;
import space.kklochko.simple_jbdd.annotations.Title;
import space.kklochko.simple_jbdd.tests.Test;
public class SimpleThenTestWithoutTitle extends Test {
@Then("The sum of 10 and 20 is 30.")
public void check() {
assert 30 == 10 + 20;
}
}
@@ -0,0 +1,6 @@
package space.kklochko.simple_jbdd.tests;
public abstract class Test {
}
@@ -0,0 +1,43 @@
package space.kklochko.simple_jbdd.tests.commands;
import space.kklochko.simple_jbdd.tests.Test;
import java.util.Map;
import java.util.TreeMap;
public abstract class AbstractTestCommand<T extends Test> {
protected String type;
protected String label;
public AbstractTestCommand(String label) {
setLabel(label);
}
public abstract boolean test();
public Map<String, String> getLabels() {
Map<String, String> labels = new TreeMap<String, String>();
labels.put(getType(), this.getLabel());
return labels;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public void setLabel(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
public abstract void setObject(T object);
public abstract T getObject();
}
@@ -0,0 +1,27 @@
package space.kklochko.simple_jbdd.tests.commands;
import space.kklochko.simple_jbdd.tests.Test;
public class SimpleTestCommand<T extends Test> extends AbstractTestCommand<T> {
protected T object;
public SimpleTestCommand(String label, T object) {
super(label);
setObject(object);
setType("Title");
}
@Override
public boolean test() {
return true;
}
public void setObject(T object) {
this.object = object;
}
public T getObject() {
return object;
}
}
@@ -0,0 +1,55 @@
package space.kklochko.simple_jbdd.tests.commands.decorators;
import space.kklochko.simple_jbdd.tests.Test;
import space.kklochko.simple_jbdd.tests.commands.AbstractTestCommand;
import java.lang.reflect.Method;
import java.util.Map;
public abstract class AbstractDecorator<T extends Test> extends AbstractTestCommand<T> {
protected AbstractTestCommand<T> command;
protected Method method;
public AbstractDecorator(String label, AbstractTestCommand<T> command) {
super(label);
setCommand(command);
}
public abstract boolean test();
public Map<String, String> getLabels() {
Map<String, String> labels = getCommand().getLabels();
labels.put(getType(), this.getLabel());
return labels;
}
public abstract boolean runMethod();
public void setMethod(Method method) {
this.method = method;
}
public Method getMethod() {
return method;
}
@Override
public void setObject(T object) {
command.setObject(object);
}
@Override
public T getObject() {
return command.getObject();
}
public AbstractTestCommand<T> getCommand() {
return command;
}
public void setCommand(AbstractTestCommand<T> command) {
this.command = command;
}
}
@@ -0,0 +1,44 @@
package space.kklochko.simple_jbdd.tests.commands.decorators;
import space.kklochko.simple_jbdd.tests.Test;
import space.kklochko.simple_jbdd.tests.commands.AbstractTestCommand;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class BlockDecorator<T extends Test> extends AbstractDecorator<T> {
public BlockDecorator(String label, AbstractTestCommand<T> command, Method method, String type) {
super(label, command);
setType(type);
setMethod(method);
}
public boolean test() {
boolean result = runMethod();
if(!result)
return false;
return getCommand().test();
}
public boolean runMethod() {
try {
method.setAccessible(true);
method.invoke(getCommand().getObject());
return true;
} catch (InvocationTargetException e) {
Throwable exception = e.getCause();
if(exception instanceof AssertionError)
throw (AssertionError) exception;
System.err.println(e.getMessage());
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
System.err.println("Check access modifiers.");
throw new RuntimeException(e);
}
};
}
@@ -0,0 +1,10 @@
package space.kklochko.simple_jbdd.tests.factories;
import space.kklochko.simple_jbdd.tests.Test;
import space.kklochko.simple_jbdd.tests.commands.AbstractTestCommand;
import space.kklochko.simple_jbdd.tests.runners.AbstractTestRunner;
public abstract class AbstractTestRunnerFactory {
public abstract AbstractTestRunner create(AbstractTestCommand<Test> command);
}
@@ -0,0 +1,13 @@
package space.kklochko.simple_jbdd.tests.factories;
import space.kklochko.simple_jbdd.tests.Test;
import space.kklochko.simple_jbdd.tests.commands.AbstractTestCommand;
import space.kklochko.simple_jbdd.tests.runners.AbstractTestRunner;
import space.kklochko.simple_jbdd.tests.runners.SimpleTestRunner;
public class SimpleTestRunnerFactory extends AbstractTestRunnerFactory {
public AbstractTestRunner create(AbstractTestCommand<Test> command) {
return new SimpleTestRunner(command);
}
}
@@ -0,0 +1,76 @@
package space.kklochko.simple_jbdd.tests.factories;
import space.kklochko.simple_jbdd.annotations.Given;
import space.kklochko.simple_jbdd.annotations.Then;
import space.kklochko.simple_jbdd.annotations.Title;
import space.kklochko.simple_jbdd.annotations.When;
import space.kklochko.simple_jbdd.tests.Test;
import space.kklochko.simple_jbdd.tests.commands.AbstractTestCommand;
import space.kklochko.simple_jbdd.tests.commands.SimpleTestCommand;
import space.kklochko.simple_jbdd.tests.commands.decorators.BlockDecorator;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.TreeMap;
public class TestCommandFactory {
public AbstractTestCommand create(Test input) {
Class<?> aClass = input.getClass();
Map<String, Method> test_methods = this.getTestMethods(aClass);
AbstractTestCommand<Test> aTestCommand = createTest(aClass, input);
if(test_methods.containsKey("Then")) {
Method then = test_methods.get("Then");
String label = then.getAnnotation(Then.class).value();
aTestCommand = new BlockDecorator<Test>(label, aTestCommand, then, "Then");
}
if(test_methods.containsKey("When")) {
Method when = test_methods.get("When");
String label = when.getAnnotation(When.class).value();
aTestCommand = new BlockDecorator<Test>(label, aTestCommand, when, "When");
}
if(test_methods.containsKey("Given")) {
Method when = test_methods.get("Given");
String label = when.getAnnotation(Given.class).value();
aTestCommand = new BlockDecorator<Test>(label, aTestCommand, when, "Given");
}
return aTestCommand;
}
protected AbstractTestCommand<Test> createTest(Class<?> aClass, Test input) {
Title aTitle = aClass.getAnnotation(Title.class);
String title = aClass.getName();
if(aTitle != null)
title = aTitle.value();
return new SimpleTestCommand<Test>(title, input);
}
protected <T extends Test> Map<String, Method> getTestMethods(Class<?> aClass) {
Map<String, Method> test_methods = new TreeMap<String, Method>();
for(Method method : aClass.getDeclaredMethods()) {
Annotation[] annotations = method.getAnnotations();
if (annotations.length == 0)
continue;
if(method.isAnnotationPresent(Given.class))
test_methods.put("Given", method);
if(method.isAnnotationPresent(When.class))
test_methods.put("When", method);
if(method.isAnnotationPresent(Then.class))
test_methods.put("Then", method);
}
return test_methods;
}
}
@@ -0,0 +1,49 @@
package space.kklochko.simple_jbdd.tests.factories;
import space.kklochko.simple_jbdd.tests.Test;
import space.kklochko.simple_jbdd.tests.commands.AbstractTestCommand;
import java.util.*;
public class TestCommandQueueFactory {
private ArrayList<Test> tests;
private TestCommandFactory factory;
public TestCommandQueueFactory(ArrayList<Test> tests) {
setTests(tests);
setFactory(new TestCommandFactory());
}
public TestCommandQueueFactory(ArrayList<Test> tests, TestCommandFactory factory) {
setTests(tests);
setFactory(factory);
}
public ArrayList<AbstractTestCommand<Test>> create() {
ArrayList<AbstractTestCommand<Test>> commands = new ArrayList<AbstractTestCommand<Test>>();
for(Test test : getTests()) {
AbstractTestCommand<Test> command = getFactory().create(test);
commands.add(command);
}
return commands;
}
public ArrayList<Test> getTests() {
return tests;
}
public void setTests(ArrayList<Test> tests) {
this.tests = tests;
}
public TestCommandFactory getFactory() {
return factory;
}
public void setFactory(TestCommandFactory factory) {
this.factory = factory;
}
}
@@ -0,0 +1,9 @@
package space.kklochko.simple_jbdd.tests.factories.meta;
import java.util.AbstractMap;
import java.util.ArrayList;
public abstract class AbstractTestCommandMetaDataFactory {
abstract public ArrayList<AbstractMap.SimpleEntry<String, String>> create();
}
@@ -0,0 +1,34 @@
package space.kklochko.simple_jbdd.tests.factories.meta;
import space.kklochko.simple_jbdd.tests.commands.AbstractTestCommand;
import space.kklochko.simple_jbdd.tests.commands.decorators.AbstractDecorator;
import java.util.AbstractMap;
import java.util.ArrayList;
public class TestCommandMetaDataFactory extends AbstractTestCommandMetaDataFactory {
private AbstractTestCommand command;
public TestCommandMetaDataFactory(AbstractTestCommand command) {
this.command = command;
}
public ArrayList<AbstractMap.SimpleEntry<String, String>> create() {
ArrayList<AbstractMap.SimpleEntry<String, String>> blocks = new ArrayList<>();
AbstractTestCommand currentCommand = command;
Class<?> aClass = command.getClass();
while(AbstractDecorator.class.isAssignableFrom(aClass)) {
AbstractDecorator block = (AbstractDecorator) currentCommand;
blocks.add(new AbstractMap.SimpleEntry<>(block.getType(), block.getLabel()));
currentCommand = block.getCommand();
aClass = currentCommand.getClass();
}
blocks.add(0, (new AbstractMap.SimpleEntry<>(currentCommand.getType(), currentCommand.getLabel())));
return blocks;
}
}
@@ -0,0 +1,8 @@
package space.kklochko.simple_jbdd.tests.loaders;
import java.util.Set;
public abstract class AbstractNameClassLoader {
public abstract Set<Class<?>> load();
}
@@ -0,0 +1,26 @@
package space.kklochko.simple_jbdd.tests.loaders;
import java.util.ArrayList;
import java.util.Set;
public class ClassLoader<T> {
AbstractNameClassLoader nameLoader;
public ClassLoader(AbstractNameClassLoader nameLoader) {
this.nameLoader = nameLoader;
}
public ArrayList<T> load() {
Set<Class<?>> names = nameLoader.load();
ArrayList<T> classes = new ArrayList<>();
for(Class<?> name : names) {
SingleClassLoader loader = new SingleClassLoader(name.getName());
classes.add((T) loader.load());
}
return classes;
}
}
@@ -0,0 +1,28 @@
package space.kklochko.simple_jbdd.tests.loaders;
import com.google.common.reflect.ClassPath;
import java.io.IOException;
import java.util.stream.Collectors;
import java.util.Set;
public class NameClassLoader extends AbstractNameClassLoader {
public Set<Class<?>> load() {
try {
return ClassPath.from(java.lang.ClassLoader.getSystemClassLoader())
.getAllClasses()
.stream()
// exclude the Test class, which indicate a Test class
.filter(aClass -> !aClass.getName().endsWith(".Test"))
.filter(aClass -> aClass.getName().endsWith("Test"))
// include classes which extends the Test class
.filter(aClass -> space.kklochko.simple_jbdd.tests.Test.class.isAssignableFrom(aClass.load()))
.filter(aClass -> aClass.getName().startsWith("space"))
.map(aClass -> aClass.load())
.collect(Collectors.toSet());
} catch (IOException e) {
return null;
}
}
}
@@ -0,0 +1,26 @@
package space.kklochko.simple_jbdd.tests.loaders;
public class SingleClassLoader {
String className;
public SingleClassLoader(String className) {
setClassName(className);
}
public Class<?> load() {
try {
return Class.forName(getClassName());
} catch (ClassNotFoundException e) {
return null;
}
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}
@@ -0,0 +1,40 @@
package space.kklochko.simple_jbdd.tests.reports;
import space.kklochko.simple_jbdd.tests.factories.meta.AbstractTestCommandMetaDataFactory;
import space.kklochko.simple_jbdd.tests.reports.fmt.AbstractTestReportFormatter;
import java.util.AbstractMap;
import java.util.ArrayList;
public abstract class AbstractReporter {
ArrayList<AbstractMap.SimpleEntry<AbstractTestCommandMetaDataFactory, Boolean>> Metadata;
AbstractTestReportFormatter formatter;
public AbstractReporter(ArrayList<AbstractMap.SimpleEntry<AbstractTestCommandMetaDataFactory, Boolean>> metadata, AbstractTestReportFormatter formatter) {
setMetadata(metadata);
setFormatter(formatter);
}
abstract public void report();
public ArrayList<AbstractMap.SimpleEntry<AbstractTestCommandMetaDataFactory, Boolean>> getMetadata() {
return Metadata;
}
public void setMetadata(ArrayList<AbstractMap.SimpleEntry<AbstractTestCommandMetaDataFactory, Boolean>> metadata) {
Metadata = metadata;
}
public void addMetadata(AbstractTestCommandMetaDataFactory testMetaData, boolean isPassed) {
getMetadata().add(new AbstractMap.SimpleEntry<>(testMetaData, isPassed));
}
public AbstractTestReportFormatter getFormatter() {
return formatter;
}
public void setFormatter(AbstractTestReportFormatter formatter) {
this.formatter = formatter;
}
}
@@ -0,0 +1,95 @@
package space.kklochko.simple_jbdd.tests.reports;
import space.kklochko.simple_jbdd.tests.factories.meta.AbstractTestCommandMetaDataFactory;
import space.kklochko.simple_jbdd.tests.reports.fmt.AbstractTestReportFormatter;
import java.util.AbstractMap;
import java.util.ArrayList;
import org.apache.commons.lang3.StringUtils;
public class CLIReporter extends AbstractReporter {
final int terminalWidth = 80;
public CLIReporter(ArrayList<AbstractMap.SimpleEntry<AbstractTestCommandMetaDataFactory, Boolean>> metadata, AbstractTestReportFormatter formatter) {
super(metadata, formatter);
}
public void report() {
ArrayList<AbstractMap.SimpleEntry<String, Boolean>> testData = format();
ArrayList<AbstractMap.SimpleEntry<String, Boolean>> failedTestData = getFailedTests(testData);
int passed = count(testData, true);
int failed = count(testData, false);
System.out.printf("%s\n", center(" Tests "));
printTests(testData);
if(failed != 0) {
System.out.printf("\n%s\n", center(" Errors "));
printTests(failedTestData);
}
System.out.printf("\n%s\n", center(" Summary "));
System.out.print(summary(passed, failed));
}
public ArrayList<AbstractMap.SimpleEntry<String, Boolean>> format() {
ArrayList<AbstractMap.SimpleEntry<String, Boolean>> data = new ArrayList<>();
for(var pair : getMetadata()) {
boolean isPassed = pair.getValue();
getFormatter().setTestMetaData(pair.getKey().create());
getFormatter().setPassed(isPassed);
data.add(new AbstractMap.SimpleEntry<>(getFormatter().format(), isPassed));
}
return data;
}
public ArrayList<AbstractMap.SimpleEntry<String, Boolean>> getFailedTests(ArrayList<AbstractMap.SimpleEntry<String, Boolean>> testData) {
ArrayList<AbstractMap.SimpleEntry<String, Boolean>> failed = new ArrayList<>();
for(var pair : testData) {
boolean isPassed = pair.getValue();
if(isPassed) continue;
failed.add(pair);
}
return failed;
}
public int count(ArrayList<AbstractMap.SimpleEntry<String, Boolean>> testData, boolean isPassedStatus) {
int count = 0;
for(var pair : testData) {
boolean isPassed = pair.getValue();
if(isPassed == isPassedStatus)
count++;
}
return count;
}
public void printTests(ArrayList<AbstractMap.SimpleEntry<String, Boolean>> testData) {
for(var pair : testData) {
System.out.print(pair.getKey());
}
}
public String center(String content) {
return StringUtils.center(content, 80, '=');
}
public String summary(int passed, int failed) {
int all = passed + failed;
double passedPercent = (double) passed / (double) all * 100.0;
double failedPercent = (double) failed / (double) all * 100.0;
return String.format("Ran %d tests.\nPassed: %d (%.2f%%)\nFailed: %d (%.2f%%)\n", all, passed, passedPercent, failed, failedPercent);
}
}
@@ -0,0 +1,33 @@
package space.kklochko.simple_jbdd.tests.reports.fmt;
import java.util.AbstractMap;
import java.util.ArrayList;
abstract public class AbstractTestReportFormatter {
private ArrayList<AbstractMap.SimpleEntry<String, String>> testMetaData;
private boolean isPassed;
public AbstractTestReportFormatter(ArrayList<AbstractMap.SimpleEntry<String, String>> testMetaData, boolean isPassed) {
setTestMetaData(testMetaData);
setPassed(isPassed);
}
public abstract String format();
public ArrayList<AbstractMap.SimpleEntry<String, String>> getTestMetaData() {
return testMetaData;
}
public void setTestMetaData(ArrayList<AbstractMap.SimpleEntry<String, String>> testMetaData) {
this.testMetaData = testMetaData;
}
public boolean isPassed() {
return isPassed;
}
public void setPassed(boolean passed) {
isPassed = passed;
}
}
@@ -0,0 +1,42 @@
package space.kklochko.simple_jbdd.tests.reports.fmt;
import space.kklochko.simple_jbdd.tests.Test;
import java.util.AbstractMap;
import java.util.ArrayList;
public class SimpleTestReportFormatter extends AbstractTestReportFormatter {
final private String passedIcon = "";
final private String failedIcon = "";
public SimpleTestReportFormatter() {
super(null, false);
}
public SimpleTestReportFormatter(ArrayList<AbstractMap.SimpleEntry<String, String>> testMetaData, boolean isPassed) {
super(testMetaData, isPassed);
}
public String format() {
String status = (isPassed()) ? getPassedIcon() : getFailedIcon();
String title = getTestMetaData().get(0).getValue();
String result = String.format("%s %s\n", status, title);
for(int i=1; i<getTestMetaData().size(); ++i) {
AbstractMap.SimpleEntry<String, String> pair = getTestMetaData().get(i);
result += String.format("\t%s %s\n", pair.getKey(), pair.getValue());
}
return result;
}
public String getPassedIcon() {
return passedIcon;
}
public String getFailedIcon() {
return failedIcon;
}
}
@@ -0,0 +1,6 @@
package space.kklochko.simple_jbdd.tests.runners;
public abstract class AbstractTestRunner {
public abstract boolean run();
}
@@ -0,0 +1,29 @@
package space.kklochko.simple_jbdd.tests.runners;
import space.kklochko.simple_jbdd.tests.Test;
import space.kklochko.simple_jbdd.tests.commands.AbstractTestCommand;
public class SimpleTestRunner extends AbstractTestRunner {
private AbstractTestCommand<Test> command;
public SimpleTestRunner(AbstractTestCommand<Test> command) {
setCommand(command);
}
public boolean run() {
try {
return getCommand().test();
}catch (AssertionError e) {
return false;
}
}
public AbstractTestCommand<Test> getCommand() {
return command;
}
public void setCommand(AbstractTestCommand<Test> command) {
this.command = command;
}
}
-57
View File
@@ -1,57 +0,0 @@
/*
* Copyright 2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import spock.lang.*
class DataDrivenSpec extends Specification {
def "maximum of two numbers"() {
expect:
Math.max(a, b) == c
where:
a << [3, 5, 9]
b << [7, 4, 9]
c << [7, 5, 9]
}
def "minimum of #a and #b is #c"() {
expect:
Math.min(a, b) == c
where:
a | b || c
3 | 7 || 3
5 | 4 || 4
9 | 9 || 9
}
def "#person.name is a #sex.toLowerCase() person"() {
expect:
person.getSex() == sex
where:
person || sex
new Person(name: "Fred") || "Male"
new Person(name: "Wilma") || "Female"
}
static class Person {
String name
String getSex() {
name == "Fred" ? "Male" : "Female"
}
}
}
-37
View File
@@ -1,37 +0,0 @@
/*
* Copyright 2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import groovy.sql.Sql
import spock.lang.Shared
import spock.lang.Specification
class DatabaseDrivenSpec extends Specification {
@Shared sql = Sql.newInstance("jdbc:h2:mem:", "org.h2.Driver")
// insert data (usually the database would already contain the data)
def setupSpec() {
sql.execute("create table maxdata (id int primary key, a int, b int, c int)")
sql.execute("insert into maxdata values (1, 3, 7, 7), (2, 5, 4, 5), (3, 9, 9, 9), (4, 2, -3, 2)")
}
def "Maximum of #a and #b is #c"() {
expect:
Math.max(a, b) == c
where:
[a, b, c] << sql.rows("select a, b, c from maxdata")
}
}
@@ -1,35 +0,0 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import spock.lang.Specification
import static spock.util.matcher.HamcrestMatchers.closeTo
/**
* Hamcrest matchers are deeply integrated with Spock's condition mechanism.
* The syntax for using a matcher in a condition is simple:
* {@code <expected-value> <matcher>}. If the condition fails, both the usual
* condition output and the matcher's output will be shown.
*
* @author Peter Niederwiser
* @since 0.5
*/
class HamcrestMatchersSpec extends Specification {
def "comparing two decimal numbers"() {
def myPi = 3.14
expect:
myPi closeTo(Math.PI, 0.01)
}
}
-30
View File
@@ -1,30 +0,0 @@
/*
* Copyright 2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import spock.lang.Specification
class HelloSpockSpec extends Specification {
def "length of Spock's and his friends' names"() {
expect:
name.size() == length
where:
name | length
"Spock" | 5
"Kirk" | 4
"Scotty" | 6
}
}
@@ -1,74 +0,0 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.lang.annotation.*
import spock.lang.Specification
/**
* Shows how methods can be included and excluded from a spec run. For
* demonstration purposes, the configuration script's location is set
* programmatically in a static initializer. Usually, the configuration
* script's location would be set externally (via the same system property),
* or the configuration script would reside in one of the two default locations:
* <li>
* <ul><tt>SpockConfig.groovy</tt> on the class path</ul>
* <ul><tt>$user_home/.spock/SpockConfig.groovy</tt> on the file system</ul>
* </li>
*
* <p>Note that the configuration script referenced by the system property
* may either reside on the class path or on the file system (make sure to
* include the whole path).
*
* <p>What's not shown here is that filtering also works for classes.
* Whereas methods can only be filtered based on annotations, classes can
* also be filtered based on their base class.
*
* <p>If you directly want to see the effect of different Spock configurations,
* just run this spec from your IDE with an additional VM parameter
* <li>
* <ul><tt>-Dspock.configuration=IncludeFastConfig.groovy</tt> or</ul>
* <ul><tt>-Dspock.configuration=ExcludeSlowConfig.groovy</tt></ul>
* </li>
*
* <p>See also the two corresponding resource files provided with this spec.
*
* @since 0.4
*/
class IncludeExcludeExtensionSpec extends Specification {
@Fast
def "a fast method"() {
println "fast"
expect: true
}
@Slow
def "a slow method"() {
println "slow"
expect: true
}
def "a neither fast nor slow method"() {
println "neither fast nor slow"
expect: true
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Fast {}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Slow {}
@@ -1,45 +0,0 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import spock.lang.*
/**
* The expected order of interactions can now be specified by using multiple
* then-blocks. Interactions in a later then-block must take place after
* interactions in an earlier then-block. The order of interactions within
* the same then-block is unspecified.
*
* @since 0.4
*/
class OrderedInteractionsSpec extends Specification {
def "collaborators must be invoked in order"() {
def coll1 = Mock(Collaborator)
def coll2 = Mock(Collaborator)
when:
// try to reverse the order of these invocations and see what happens
coll1.collaborate()
coll2.collaborate()
then:
1 * coll1.collaborate()
then:
1 * coll2.collaborate()
}
}
interface Collaborator {
def collaborate()
}
@@ -1,64 +0,0 @@
/*
* Copyright 2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import spock.lang.Specification
class Publisher {
def subscribers = []
def send(event) {
subscribers.each {
try {
it.receive(event)
} catch (Exception e) {}
}
}
}
interface Subscriber {
def receive(event)
}
class PublisherSpec extends Specification {
def pub = new Publisher()
def sub1 = Mock(Subscriber)
def sub2 = Mock(Subscriber)
def setup() {
pub.subscribers << sub1 << sub2
}
def "delivers events to all subscribers"() {
when:
pub.send("event")
then:
1 * sub1.receive("event")
1 * sub2.receive("event")
}
def "can cope with misbehaving subscribers"() {
sub1.receive(_) >> { throw new Exception() }
when:
pub.send("event1")
pub.send("event2")
then:
1 * sub2.receive("event1")
1 * sub2.receive("event2")
}
}
@@ -1,41 +0,0 @@
/*
* Copyright 2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import spock.lang.*
abstract class BaseSpec extends Specification {
def x = { println 'base field initializer' }()
def setupSpec() { println 'base setupSpec()' }
def cleanupSpec() { println 'base cleanupSpec()' }
def setup() { println 'base setup()' }
def cleanup() { println 'base cleanup()' }
def baseSpecMethod() { setup: println 'base spec method' }
}
class DerivedSpec extends BaseSpec {
def y = { println 'derived field initializer' }()
def setupSpec() { println 'derived setupSpec()' }
def cleanupSpec() { println 'derived cleanupSpec()' }
def setup() { println 'derived setup()' }
def cleanup() { println 'derived cleanup()' }
def derivedSpecMethod() { setup: println 'derived spec method' }
}
-120
View File
@@ -1,120 +0,0 @@
/*
* Copyright 2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import spock.lang.Specification
class EmptyStackSpec extends Specification {
def stack = new Stack()
def "size"() {
expect: stack.size() == 0
}
def "pop"() {
when: stack.pop()
then: thrown(EmptyStackException)
}
def "peek"() {
when: stack.peek()
then: thrown(EmptyStackException)
}
def "push"() {
when:
stack.push("elem")
then:
stack.size() == old(stack.size()) + 1
stack.peek() == "elem"
}
}
class StackWithOneElementSpec extends Specification {
def stack = new Stack()
def setup() {
stack.push("elem")
}
def "size"() {
expect: stack.size() == 1
}
def "pop"() {
when:
def x = stack.pop()
then:
x == "elem"
stack.size() == 0
}
def "peek"() {
when:
def x = stack.peek()
then:
x == "elem"
stack.size() == 1
}
def "push"() {
when:
stack.push("elem2")
then:
stack.size() == 2
stack.peek() == "elem2"
}
}
class StackWithThreeElementsSpec extends Specification {
def stack = new Stack()
def setup() {
["elem1", "elem2", "elem3"].each { stack.push(it) }
}
def "size"() {
expect: stack.size() == 3
}
def "pop"() {
expect:
stack.pop() == "elem3"
stack.pop() == "elem2"
stack.pop() == "elem1"
stack.size() == 0
}
def "peek"() {
expect:
stack.peek() == "elem3"
stack.peek() == "elem3"
stack.peek() == "elem3"
stack.size() == 3
}
def "push"() {
when:
stack.push("elem4")
then:
stack.size() == 4
stack.peek() == "elem4"
}
}
@@ -1,42 +0,0 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import spock.lang.*
/**
* Demonstrates how {@code @Stepwise} causes a spec to be run in incremental steps.
* Change a step's condition from {@literal true} to {@literal false}, and observe
* how the remaining steps will be skipped automatically on the next run.
* Also notice that if you run a single step (e.g. from the IDE's context menu),
* all prior steps will also be run.
*
* <p>{@code @Stepwise} is particularly useful for higher-level specs whose
* methods have logical dependencies.
*
* @since 0.4
*/
@Stepwise
class StepwiseExtensionSpec extends Specification {
def "step 1"() {
expect: true // try to change this to 'false'
}
def "step 2"() {
expect: true
}
def "step 3"() {
expect: true
}
}
@@ -1,27 +0,0 @@
/*
* Copyright 2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.junit.Rule
import org.junit.rules.TestName
import spock.lang.Specification
class UsingJUnitRulesSpec extends Specification {
@Rule TestName name
def "retrieve test name at runtime"() {
println "entering '$name.methodName'"
expect: 1 + 1 == 2
println "leaving '$name.methodName'"
}
}
@@ -1,5 +1,6 @@
package space.kklochko.simple_jbdd.test_examples
package space.kklochko.simple_jbdd.test_examples.samples
import space.kklochko.simple_jbdd.test_examples.samples.Sum
import spock.lang.Specification
class SumSpec extends Specification {
@@ -0,0 +1,54 @@
package space.kklochko.simple_jbdd.tests.factories
import space.kklochko.simple_jbdd.test_examples.tests.SimpleGivenWhenThenTest
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTest
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTestWithoutTitle
import space.kklochko.simple_jbdd.tests.Test
import spock.lang.Narrative
import spock.lang.Specification
import spock.lang.Subject
import spock.lang.Title
@Narrative("""The factory must generate a queue of the test commands, so
those tests check if factory generate them right.
""")
@Title("Integrated tests for TestCommandQueueFactory")
class IntegratedTestCommandQueueFactorySpec extends Specification {
def "Factory generate the queue and its size is 2."() {
given: "I have a list of tests"
def tests = [new SimpleThenTest(), new SimpleThenTestWithoutTitle()]
and: "I have a test factory"
def testFactory = new TestCommandFactory()
and: "I have a factory"
@Subject
def factory = new TestCommandQueueFactory(tests, testFactory)
when: "Factory build the queue"
def queue = factory.create()
then: "the size is 2"
2 == queue.size()
}
def "Factory generate the queue and the commands and test order is the same."() {
given: "I have a list of tests with first methods [check, setup]"
def tests = [new SimpleThenTest(), new SimpleGivenWhenThenTest()]
and: "I have a test factory"
def testFactory = new TestCommandFactory()
and: "I have a factory"
@Subject
def factory = new TestCommandQueueFactory(tests, testFactory)
when: "Factory build the queue"
def queue = factory.create()
then: "the order is the same"
queue[0].method.getName() == "check"
queue[1].method.getName() == "setup"
}
}
@@ -0,0 +1,96 @@
package space.kklochko.simple_jbdd.tests.factories
import space.kklochko.simple_jbdd.test_examples.tests.SimpleGivenWhenThenTest
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTest
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTestWithoutTitle
import space.kklochko.simple_jbdd.tests.factories.TestCommandFactory
import spock.lang.Narrative;
import spock.lang.Specification
import spock.lang.Subject
import spock.lang.Title
@Narrative("""The factory must generate a test command, so
those tests check if factory generate them right.
""")
@Title("Unit tests for TestCommandFactory")
class TestCommandFactorySpec extends Specification {
def "If the map of methods have one test block."() {
given: "I have a TestCommandFactory object"
@Subject
def factory = new TestCommandFactory();
and: "I also have a SimpleThenTest object"
def simpleThenTest = new SimpleThenTest()
when: "If a test have only one block"
def map = factory.getTestMethods(simpleThenTest.getClass())
then: "the map size is 1 method"
1 == map.size()
}
def "createTest() sets the title to a class name if no Title annotation."() {
given: "I have a test factory"
@Subject
def factory = new TestCommandFactory()
and: "I have a test class without Title"
def testClass = new SimpleThenTestWithoutTitle()
when: "I build a test"
def test = factory.create(testClass)
then: "The test have title as the test class"
testClass.class.getName() == test.getLabels()['Title']
}
def "createTest() sets the title to a value of the Title annotation."() {
given: "I have a test factory"
@Subject
def factory = new TestCommandFactory()
and: "I have a test class with Title"
def testClass = new SimpleThenTest()
and: "and the title of the annotation"
def titleClass = space.kklochko.simple_jbdd.annotations.Title.class
def title = testClass.getClass().getAnnotation(titleClass).value()
when: "I build a test"
def test = factory.create(testClass)
then: "The test have title as the test class"
title == test.getLabels()['Title']
}
def "createTest() build a Test with only Then."() {
given: "I have a test factory"
@Subject
def factory = new TestCommandFactory()
and: "I have a test class with Title"
def testClass = new SimpleThenTest()
when: "I build a test"
def test = factory.create(testClass)
then: "The test have title and then method"
['Title', 'Then'].toSet() == test.getLabels().keySet()
}
def "createTest() build a Test with Given-When-Then."() {
given: "I have a test factory"
@Subject
def factory = new TestCommandFactory()
and: "I have a test class with Title and Given-When-Then"
def testClass = new SimpleGivenWhenThenTest()
when: "I build a test"
def test = factory.create(testClass)
then: "The test have title and then method"
['Title', 'Given', 'When', 'Then'].toSet() == test.getLabels().keySet()
}
}
@@ -0,0 +1,36 @@
package space.kklochko.simple_jbdd.tests.factories
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTest
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTestWithoutTitle
import space.kklochko.simple_jbdd.tests.Test
import space.kklochko.simple_jbdd.tests.commands.decorators.BlockDecorator
import spock.lang.Narrative
import spock.lang.Specification
import spock.lang.Subject
import spock.lang.Title
@Narrative("""The factory must generate a queue of the test commands, so
those tests check if factory generate them right.
""")
@Title("Unit tests for TestCommandQueueFactory")
class TestCommandQueueFactorySpec extends Specification {
def "Factory generate the queue and its size is 2."() {
given: "I have a list of tests"
def tests = [Mock(SimpleThenTest), Mock(SimpleThenTestWithoutTitle)]
and: "I have a test factory"
def testFactory = Stub(TestCommandFactory)
testFactory.create() >>> [Mock(BlockDecorator<Test>)]*2
and: "I have the factory"
@Subject
def factory = new TestCommandQueueFactory(tests, testFactory)
when: "Factory build the queue"
def queue = factory.create()
then: "the size is 2"
2 == queue.size()
}
}
@@ -0,0 +1,46 @@
package space.kklochko.simple_jbdd.tests.factories.meta
import space.kklochko.simple_jbdd.test_examples.tests.SimpleFailedThenTest
import space.kklochko.simple_jbdd.test_examples.tests.SimpleGivenWhenThenTest
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTest
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTestWithoutTitle
import space.kklochko.simple_jbdd.tests.commands.AbstractTestCommand
import space.kklochko.simple_jbdd.tests.commands.decorators.BlockDecorator
import space.kklochko.simple_jbdd.tests.factories.TestCommandFactory
import space.kklochko.simple_jbdd.tests.runners.SimpleTestRunner
import spock.lang.Narrative
import spock.lang.Specification
import spock.lang.Subject
import spock.lang.Title
@Narrative("""The factory must generate a test command metadata, so
those tests check if the factory generate it right.
""")
@Title("Integrated tests for TestCommandMetaDataFactory")
class IntegratedTestCommandMetaDataFactorySpec extends Specification {
def "Metadata have the map of methods have one test block."() {
given: "I have a command factory"
def commandFactory = new TestCommandFactory();
and: "I have a command of a test"
def command = commandFactory.create(testObject)
and: "I have a TestCommandMetaDataFactory object"
@Subject
def factory = new TestCommandMetaDataFactory(command)
when: "Parse the command to get metadata"
def metadata = factory.create()
then: "the metadata have only two labels for Title and Then"
expectedCount == metadata.size()
where: "Possible variants of tests"
testObject || expectedCount
new SimpleThenTest() || 2
new SimpleGivenWhenThenTest() || 4
new SimpleThenTestWithoutTitle() || 2
new SimpleFailedThenTest() || 2
}
}
@@ -0,0 +1,93 @@
package space.kklochko.simple_jbdd.tests.factories.meta
import space.kklochko.simple_jbdd.test_examples.tests.SimpleGivenWhenThenTest
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTest
import space.kklochko.simple_jbdd.tests.commands.AbstractTestCommand
import space.kklochko.simple_jbdd.tests.commands.decorators.BlockDecorator
import space.kklochko.simple_jbdd.tests.factories.TestCommandFactory
import spock.lang.Narrative;
import spock.lang.Specification
import spock.lang.Subject
import spock.lang.Title
@Narrative("""The factory must generate a test command metadata, so
those tests check if the factory generate it right.
""")
@Title("Unit tests for TestCommandMetaDataFactory")
class TestCommandMetaDataFactorySpec extends Specification {
def "If the map of methods have one test block."() {
given: "I have a simple test with only Then block and its command."
def test = Mock(AbstractTestCommand) {
getType() >> "Title"
getLabel() >> "A simple test"
}
def command = GroovyMock(BlockDecorator) {
getType() >> "Then"
getLabel() >> "Then block"
getCommand() >> test
}
and: "I have a TestCommandMetaDataFactory object"
@Subject
def factory = new TestCommandMetaDataFactory(command)
when: "Parse the command to get metadata"
def metadata = factory.create()
then: "The metadata have only two labels for Title and Then"
2 == metadata.size()
and: "The format and the order must be the same"
metadata == [
new AbstractMap.SimpleEntry<>("Title", "A simple test"),
new AbstractMap.SimpleEntry<>("Then", "Then block")
]
}
def "If the map of methods have GivenWhenThen blocks."() {
given: "I have a simple test with only Then block and its command."
def test = Mock(AbstractTestCommand) {
getType() >> "Title"
getLabel() >> "A simple test"
}
and: "Bootstrapping the test command"
def then = GroovyMock(BlockDecorator) {
getType() >> "Then"
getLabel() >> "Then block"
getCommand() >> test
}
def when = GroovyMock(BlockDecorator) {
getType() >> "When"
getLabel() >> "When block"
getCommand() >> then
}
def command = GroovyMock(BlockDecorator) {
getType() >> "Given"
getLabel() >> "Given block"
getCommand() >> when
}
and: "I have a TestCommandMetaDataFactory object"
@Subject
def factory = new TestCommandMetaDataFactory(command)
when: "Parse the command to get metadata"
def metadata = factory.create()
then: "The metadata have only two labels for Title and Then"
4 == metadata.size()
and: "The format and the order must be the same"
metadata == [
new AbstractMap.SimpleEntry<>("Title", "A simple test"),
new AbstractMap.SimpleEntry<>("Given", "Given block"),
new AbstractMap.SimpleEntry<>("When", "When block"),
new AbstractMap.SimpleEntry<>("Then", "Then block")
]
}
}
@@ -0,0 +1,43 @@
package space.kklochko.simple_jbdd.tests.loaders
import spock.lang.Narrative
import spock.lang.Specification
import spock.lang.Subject
import spock.lang.Title
@Narrative("""The loader must return a test classes or null, so
those tests check if responses are right.
""")
@Title("Integrated tests for ClassLoader.")
class IntegratedClassLoader extends Specification {
def "The classes have been loaded."() {
given: "I have a name class loader"
def nameLoader = new NameClassLoader()
and: "I have a loader for classes"
@Subject
def loader = new ClassLoader(nameLoader)
when: "The classes were loaded"
def classes = loader.load()
then: "Checking that the test result is expected"
3 == classes.size()
}
def "The classes must be represent the Test interface."() {
given: "I have a name class loader"
def nameLoader = new NameClassLoader()
and: "I have a loader for classes"
@Subject
def loader = new ClassLoader(nameLoader)
when: "The classes were loaded"
def classes = loader.load()
then: "Checking that the test result is expected"
classes.every({ space.kklochko.simple_jbdd.tests.Test.class.isAssignableFrom(it) })
}
}
@@ -0,0 +1,25 @@
package space.kklochko.simple_jbdd.tests.loaders
import spock.lang.Narrative
import spock.lang.Specification
import spock.lang.Subject
import spock.lang.Title
@Narrative("""The loader must return a test class names or null, so
those tests check if responses are right.
""")
@Title("Integrated tests for NameClassLoader.")
class IntegratedNameClassLoader extends Specification {
def "The class names have been loaded."() {
given: "I have a class loader"
@Subject
def loader = new NameClassLoader()
when: "The class names were loaded"
def classNames = loader.load()
then: "Checking that the test result is expected"
3 == classNames.size()
}
}
@@ -0,0 +1,31 @@
package space.kklochko.simple_jbdd.tests.loaders
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTest
import spock.lang.Narrative
import spock.lang.Specification
import spock.lang.Subject
import spock.lang.Title
@Narrative("""The loader must return a class or null, so
those tests check if responses are right.
""")
@Title("Integrated tests for SingleClassLoader.")
class IntegratedSingleClassLoader extends Specification {
def "The class has been loaded."() {
given: "I have a class loader"
@Subject
def loader = new SingleClassLoader(className)
when: "The class was loaded"
def aClass = loader.load()
then: "Checking that the test result is expected"
aClass == expectedClass
where: "Possible variants of tests"
className || expectedClass
"fourHundredFour" || null
"space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTest" || SimpleThenTest
}
}
@@ -0,0 +1,72 @@
package space.kklochko.simple_jbdd.tests.reports
import space.kklochko.simple_jbdd.tests.factories.meta.AbstractTestCommandMetaDataFactory
import space.kklochko.simple_jbdd.tests.reports.fmt.AbstractTestReportFormatter
import space.kklochko.simple_jbdd.tests.reports.fmt.SimpleTestReportFormatter
import spock.lang.Narrative
import spock.lang.Specification
import spock.lang.Subject
import spock.lang.Title
@Narrative("""The reporter must generate a report in the same format, so
those tests check if the reporter generate it right.
""")
@Title("Unit tests for CLIReporter.format()")
class CLIReporterFormatMethodSpec extends Specification {
private metaData
def setup() {
def factory = Mock(AbstractTestCommandMetaDataFactory) {
create() >> [
new AbstractMap.SimpleEntry<>("Title", "A simple test"),
new AbstractMap.SimpleEntry<>("Then", "Then block")
]
}
def metaData = [
new AbstractMap.SimpleEntry<>(factory, true),
new AbstractMap.SimpleEntry<>(factory, false)
]
this.metaData = metaData
}
def "Format function use formatter twice."() {
given: "I have a formatter"
def formatter = Mock(AbstractTestReportFormatter)
and: "I have a reporter"
@Subject
def reporter = new CLIReporter(metaData, formatter)
when: "Generate the summary format"
def formattedTestData = reporter.format()
then: "The methods must called 2 times"
2 * formatter.format()
2 * formatter.setPassed(_)
2 * formatter.setTestMetaData(_)
and: "The data must have 2 elements"
2 == formattedTestData.size()
}
def "Format function returns the formatted data."() {
given: "I have a formatter"
def formatter = new SimpleTestReportFormatter()
and: "I have a reporter"
@Subject
def reporter = new CLIReporter(metaData, formatter)
when: "Generate the summary format"
def formattedTestData = reporter.format()
then: "The format must have not null values"
formattedTestData.every({ it.getKey() != null })
and: "The data must have 2 elements"
2 == formattedTestData.size()
}
}
@@ -0,0 +1,77 @@
package space.kklochko.simple_jbdd.tests.reports
import space.kklochko.simple_jbdd.tests.factories.meta.AbstractTestCommandMetaDataFactory
import space.kklochko.simple_jbdd.tests.reports.fmt.AbstractTestReportFormatter
import space.kklochko.simple_jbdd.tests.reports.fmt.SimpleTestReportFormatter
import spock.lang.Narrative
import spock.lang.Specification
import spock.lang.Subject
import spock.lang.Title
@Narrative("""The reporter must generate a report in the same format, so
those tests check if the reporter generate it right.
""")
@Title("Unit tests for CLIReporter")
class CLIReporterSpec extends Specification {
def "Function, getFailedTest, returns the data of failed tests."() {
given: "I have a reporter"
@Subject
def reporter = new CLIReporter(null, null)
and: "I have a testData"
def passedTestPair = new AbstractMap.SimpleEntry<>("Passed", true)
def failedTestPair = new AbstractMap.SimpleEntry<>("Failed", false)
def testData = Collections.nCopies(countPassed, passedTestPair) + Collections.nCopies(countFailed, failedTestPair)
when: "Generate the summary format"
def failedTestData = reporter.getFailedTests(testData)
then: "The format must be the same"
failedTestData.every({ it.getKey() == "Failed" && it.getValue() == false})
expectedFailedCount == failedTestData.size()
where: "Possible variants of tests"
countPassed | countFailed || expectedFailedCount
5 | 10 || 10
}
def "Count function returns the count of passed or failed tests."() {
given: "I have a reporter"
@Subject
def reporter = new CLIReporter(null, null)
and: "I have a testData"
def passedTestPair = new AbstractMap.SimpleEntry<>("Passed", true)
def failedTestPair = new AbstractMap.SimpleEntry<>("Failed", false)
def testData = Collections.nCopies(countPassed, passedTestPair) + Collections.nCopies(countFailed, failedTestPair)
when: "Generate the summary format"
def count = reporter.count(testData, isPassed)
then: "The format must be the same"
expectedCount == count
where: "Possible variants of tests"
countPassed | countFailed | isPassed || expectedCount
10 | 20 | true || 10
10 | 20 | false || 20
}
def "Summary format."() {
given: "I have a reporter"
@Subject
def reporter = new CLIReporter(null, null)
when: "Generate the summary format"
def format = reporter.summary(passed, failed)
then: "The format must be the same"
expectedFormat == format
where: "Possible variants of tests"
passed | failed || expectedFormat
10 | 20 || "Ran 30 tests.\nPassed: 10 (33.33%)\nFailed: 20 (66.67%)\n"
10 | 10 || "Ran 20 tests.\nPassed: 10 (50.00%)\nFailed: 10 (50.00%)\n"
}
}
@@ -0,0 +1,68 @@
package space.kklochko.simple_jbdd.tests.reports.fmt
import space.kklochko.simple_jbdd.test_examples.tests.SimpleFailedThenTest
import space.kklochko.simple_jbdd.test_examples.tests.SimpleGivenWhenThenTest
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTest
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTestWithoutTitle
import space.kklochko.simple_jbdd.tests.commands.AbstractTestCommand
import space.kklochko.simple_jbdd.tests.commands.decorators.BlockDecorator
import space.kklochko.simple_jbdd.tests.factories.meta.TestCommandMetaDataFactory
import spock.lang.Narrative
import spock.lang.Specification
import spock.lang.Subject
import spock.lang.Title
@Narrative("""The formatter must generate a format for the test command metadata, so
those tests check if the formatter generate it right.
""")
@Title("Unit tests for SimpleTestReportFormatter")
class SimpleTestReportFormatterSpec extends Specification {
def "If the map of methods have one test block."() {
given: "I have a metaData"
def metaData = [
new AbstractMap.SimpleEntry<>("Title", "A simple test"),
new AbstractMap.SimpleEntry<>("Then", "Then block")
]
and: "I have the formatter object"
@Subject
def formatter = new SimpleTestReportFormatter(metaData, isPassed)
when: "Generate the format"
def format = formatter.format()
then: "The format must be the same"
expectedFormat == format
where: "Possible variants of tests"
isPassed || expectedFormat
true || "✓ A simple test\n\tThen Then block\n"
false || " A simple test\n\tThen Then block\n"
}
def "If the map of methods have GivenWhenThen blocks."() {
given: "I have a metaData"
def metaData = [
new AbstractMap.SimpleEntry<>("Title", "A simple test"),
new AbstractMap.SimpleEntry<>("Given", "Given block"),
new AbstractMap.SimpleEntry<>("When", "When block"),
new AbstractMap.SimpleEntry<>("Then", "Then block")
]
and: "I have the formatter object"
@Subject
def formatter = new SimpleTestReportFormatter(metaData, isPassed)
when: "Generate the format"
def format = formatter.format()
then: "The format must be the same"
expectedFormat == format
where: "Possible variants of tests"
isPassed || expectedFormat
true || "✓ A simple test\n\tGiven Given block\n\tWhen When block\n\tThen Then block\n"
false || " A simple test\n\tGiven Given block\n\tWhen When block\n\tThen Then block\n"
}
}
@@ -0,0 +1,42 @@
package space.kklochko.simple_jbdd.tests.runners
import space.kklochko.simple_jbdd.test_examples.tests.SimpleFailedThenTest
import space.kklochko.simple_jbdd.test_examples.tests.SimpleGivenWhenThenTest
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTest
import space.kklochko.simple_jbdd.test_examples.tests.SimpleThenTestWithoutTitle
import space.kklochko.simple_jbdd.tests.factories.TestCommandFactory
import spock.lang.Narrative
import spock.lang.Specification
import spock.lang.Subject
import spock.lang.Title
@Narrative("""The runner must return a result of test, so
those tests check if passed and failed test generate the right result.
""")
@Title("Integrated tests for SimpleTestRunner with TestCommandFactory and test examples")
class IntegratedSimpleTestRunnerSpec extends Specification {
def "The real test has been run."() {
given: "I have a factory"
def factory = new TestCommandFactory();
and: "I have a testCommand created from a test object"
def test = factory.create(testObject)
and: "I have a test runner"
@Subject
def runner = new SimpleTestRunner(test)
when: "The testCommand has been run"
def result = runner.run()
then: "Checking that the test result is expected"
result == expectedResult
where: "Possible variants of tests"
testObject || expectedResult
new SimpleThenTest() || true
new SimpleGivenWhenThenTest() || true
new SimpleThenTestWithoutTitle() || true
new SimpleFailedThenTest() || false
}
}
@@ -0,0 +1,47 @@
package space.kklochko.simple_jbdd.tests.runners
import space.kklochko.simple_jbdd.tests.commands.SimpleTestCommand
import space.kklochko.simple_jbdd.tests.factories.TestCommandFactory
import spock.lang.Narrative
import spock.lang.Specification
import spock.lang.Subject
import spock.lang.Title
@Narrative("""The runner must return a result of test, so
those tests check if passed and failed test generate the right result.
""")
@Title("Unit tests for SimpleTestRunner")
class SimpleTestRunnerSpec extends Specification {
def "The test with then has been passed."() {
given: "I have a testCommand"
def test = Stub(SimpleTestCommand)
test.test() >>> [true]
and: "I have a test runner"
@Subject
def runner = new SimpleTestRunner(test)
when: "The testCommand has been run"
def result = runner.run()
then: "The result is true"
result
}
def "The test with then that has not been passed."() {
given: "I have a failed testCommand"
def test = Stub(SimpleTestCommand)
test.test() >> { assert 1 == 0 }
and: "I have a test runner"
@Subject
def runner = new SimpleTestRunner(test)
when: "The testCommand has been run"
def result = runner.run()
then: "The result is false"
!result
}
}