Compare commits

...
13 Commits
Author SHA1 Message Date
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
20 changed files with 665 additions and 1 deletions
+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,44 @@
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, 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,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,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"
}
}
@@ -12,7 +12,7 @@ import spock.lang.Title
@Narrative("""The factory must generate a test command, so
those tests check if factory generate them right.
""")
@Title("Unit test for TestCommandFactory")
@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"
@@ -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 ClassLoader.")
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,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"
}
}