Compare commits

...
9 Commits
Author SHA1 Message Date
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
12 changed files with 444 additions and 4 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
@@ -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,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);
}
};
}
@@ -2,8 +2,12 @@ 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;
@@ -11,9 +15,41 @@ import java.util.Map;
import java.util.TreeMap;
public class TestCommandFactory {
public <T extends Test> void create(T input) {
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) {
@@ -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;
}
}
@@ -1,12 +1,22 @@
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.tests.factories.TestCommandFactory;
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 test 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"
@@ -18,5 +28,69 @@ class TestCommandFactorySpec extends Specification {
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,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
}
}