Commit cc92ba1a authored by Greg Turnquist's avatar Greg Turnquist Committed by Andy Wilkinson

Fix spring test to properly handle failures

Failing test cases weren't not properly handled and instead caused
test to break. Added a test case and verified it works for both JUnit
and Spock.
parent 7e7d4b7d
class FailingJUnitTests {
@Test
void passingTest() {
assertTrue(true)
}
@Test
void failureByAssertion() {
assertTrue(false)
}
@Test
void failureByException() {
throw new RuntimeException("This should also be handled")
}
}
class FailingSpockTest extends Specification {
def "this should pass"() {
expect:
name.size() == length
where:
name | length
"Spock" | 5
}
def "this should fail on purpose as well"() {
when:
String text = "Greetings"
then:
//throw new RuntimeException("This should fail!")
true == false
}
}
\ No newline at end of file
...@@ -48,20 +48,22 @@ class JUnitTester extends AbstractTester { ...@@ -48,20 +48,22 @@ class JUnitTester extends AbstractTester {
} }
@Override @Override
protected TestResults test(Class<?>[] testable) { protected TestResults test(List<Class<?>> testable) {
Result results = JUnitCore.runClasses(testable) return JUnitTester.runEmbeddedTests(testable)
}
static TestResults runEmbeddedTests(List<Class<?>> testable) {
Result results = JUnitCore.runClasses(testable.toArray(new Class<?>[0]))
TestResults testResults = new TestResults() TestResults testResults = new TestResults()
testResults.setFailureCount(results.getFailureCount())
testResults.setRunCount(results.getRunCount()) testResults.setRunCount(results.getRunCount())
List<org.springframework.boot.cli.command.tester.Failure> failures = List<Failure> failures = new ArrayList<Failure>()
new ArrayList<Failure>()
for (org.junit.runner.notification.Failure failure : results.getFailures()) { for (org.junit.runner.notification.Failure failure : results.getFailures()) {
failures.add(new Failure(failure.getDescription().toString(), failure.getTrace())) failures.add(new Failure(failure.exception.toString(), failure.trace))
} }
testResults.setFailures(failures.toArray(new Failure[0])) testResults.setFailures(failures)
return testResults return testResults
} }
......
...@@ -14,16 +14,15 @@ ...@@ -14,16 +14,15 @@
* limitations under the License. * limitations under the License.
*/ */
import org.junit.runner.Result
import org.springframework.boot.cli.command.tester.Failure
import org.springframework.boot.cli.command.tester.TestResults import org.springframework.boot.cli.command.tester.TestResults
import spock.lang.Specification import spock.lang.Specification
import spock.util.EmbeddedSpecRunner
/** /**
* Groovy script to run Spock tests inside the {@link TestCommand}. * Groovy script to run Spock tests inside the {@link TestCommand}.
* Needs to be compiled along with the actual code to work properly. * Needs to be compiled along with the actual code to work properly.
* *
* NOTE: SpockTester depends on JUnitTester to actually run the tests
*
* @author Greg Turnquist * @author Greg Turnquist
*/ */
class SpockTester extends AbstractTester { class SpockTester extends AbstractTester {
...@@ -41,22 +40,8 @@ class SpockTester extends AbstractTester { ...@@ -41,22 +40,8 @@ class SpockTester extends AbstractTester {
} }
@Override @Override
protected TestResults test(Class<?>[] testable) { protected TestResults test(List<Class<?>> testable) {
Result results = new EmbeddedSpecRunner().runClasses(Arrays.asList(testable)) return JUnitTester.runEmbeddedTests(testable)
TestResults testResults = new TestResults()
testResults.setFailureCount(results.getFailureCount())
testResults.setRunCount(results.getRunCount())
List<org.springframework.boot.cli.command.tester.Failure> failures =
new ArrayList<Failure>()
for (org.junit.runner.notification.Failure failure : results.getFailures()) {
failures.add(new Failure(failure.getDescription().toString(), failure.getTrace()))
}
testResults.setFailures(failures.toArray(new Failure[0]))
return testResults
} }
} }
...@@ -32,11 +32,11 @@ public abstract class AbstractTester { ...@@ -32,11 +32,11 @@ public abstract class AbstractTester {
return TestResults.NONE return TestResults.NONE
} }
return test(testable.toArray(new Class<?>[0])) return test(new ArrayList<Class<?>>(testable))
} }
protected abstract Set<Class<?>> findTestableClasses(List<Class<?>> compiled) protected abstract Set<Class<?>> findTestableClasses(List<Class<?>> compiled)
protected abstract TestResults test(Class<?>[] testable) protected abstract TestResults test(List<Class<?>> testable)
} }
...@@ -88,7 +88,6 @@ public class TestCommand extends OptionParsingCommand { ...@@ -88,7 +88,6 @@ public class TestCommand extends OptionParsingCommand {
private static class TestOptionHandler extends OptionHandler { private static class TestOptionHandler extends OptionHandler {
private final GroovyCompiler compiler; private final GroovyCompiler compiler;
private TestResults results; private TestResults results;
public TestOptionHandler() { public TestOptionHandler() {
...@@ -113,11 +112,12 @@ public class TestCommand extends OptionParsingCommand { ...@@ -113,11 +112,12 @@ public class TestCommand extends OptionParsingCommand {
* against the composite AST. * against the composite AST.
*/ */
// Compile - Pass 1 - collect testers // Compile - Pass 1 - compile source code to see what test libraries were
// pulled in
Object[] sources = this.compiler.sources(fileOptions.getFilesArray()); Object[] sources = this.compiler.sources(fileOptions.getFilesArray());
Set<File> testerFiles = compileAndCollectTesterFiles(sources); List<File> testerFiles = compileAndCollectTesterFiles(sources);
// Compile - Pass 2 - with appropriate testers added in // Compile - Pass 2 - add appropriate testers
List<File> files = new ArrayList<File>(fileOptions.getFiles()); List<File> files = new ArrayList<File>(fileOptions.getFiles());
files.addAll(testerFiles); files.addAll(testerFiles);
sources = this.compiler.sources(files.toArray(new File[files.size()])); sources = this.compiler.sources(files.toArray(new File[files.size()]));
...@@ -145,14 +145,17 @@ public class TestCommand extends OptionParsingCommand { ...@@ -145,14 +145,17 @@ public class TestCommand extends OptionParsingCommand {
GroovyObject obj = (GroovyObject) tester.newInstance(); GroovyObject obj = (GroovyObject) tester.newInstance();
this.results.add((TestResults) obj.invokeMethod("findAndTest", compiled)); this.results.add((TestResults) obj.invokeMethod("findAndTest", compiled));
} }
printReport(this.results); printReport(this.results);
} }
private Set<File> compileAndCollectTesterFiles(Object[] sources) private List<File> compileAndCollectTesterFiles(Object[] sources)
throws CompilationFailedException, IOException { throws CompilationFailedException, IOException {
Set<File> testerFiles = new LinkedHashSet<File>(); Set<String> testerUnits = new LinkedHashSet<String>();
addTesterOnClass(sources, "org.junit.Test", "junit", testerFiles); List<File> testerFiles = new ArrayList<File>();
addTesterOnClass(sources, "spock.lang.Specification", "spock", testerFiles); addTesterOnClass(sources, "org.junit.Test", testerFiles, testerUnits, "junit");
addTesterOnClass(sources, "spock.lang.Specification", testerFiles,
testerUnits, "junit", "spock");
if (!testerFiles.isEmpty()) { if (!testerFiles.isEmpty()) {
testerFiles.add(createTempTesterFile("tester")); testerFiles.add(createTempTesterFile("tester"));
} }
...@@ -161,12 +164,16 @@ public class TestCommand extends OptionParsingCommand { ...@@ -161,12 +164,16 @@ public class TestCommand extends OptionParsingCommand {
} }
private void addTesterOnClass(Object[] sources, String className, private void addTesterOnClass(Object[] sources, String className,
String testerName, Set<File> testerFiles) { List<File> testerFiles, Set<String> testerUnits, String... testerNames) {
for (Object source : sources) { for (Object source : sources) {
if (source instanceof Class<?>) { if (source instanceof Class<?>) {
try { try {
((Class<?>) source).getClassLoader().loadClass(className); ((Class<?>) source).getClassLoader().loadClass(className);
for (String testerName : testerNames) {
if (testerUnits.add(testerName)) {
testerFiles.add(createTempTesterFile(testerName)); testerFiles.add(createTempTesterFile(testerName));
}
}
return; return;
} }
catch (ClassNotFoundException ex) { catch (ClassNotFoundException ex) {
...@@ -201,7 +208,7 @@ public class TestCommand extends OptionParsingCommand { ...@@ -201,7 +208,7 @@ public class TestCommand extends OptionParsingCommand {
String trailer = ""; String trailer = "";
String trace = ""; String trace = "";
for (Failure failure : results.getFailures()) { for (Failure failure : results.getFailures()) {
trailer += failure.getDescription().toString(); trailer += "Failed: " + failure.getDescription().toString() + "\n";
trace += failure.getTrace() + "\n"; trace += failure.getTrace() + "\n";
} }
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
package org.springframework.boot.cli.command.tester; package org.springframework.boot.cli.command.tester;
import java.util.Arrays; import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
...@@ -42,8 +42,8 @@ public class TestResults { ...@@ -42,8 +42,8 @@ public class TestResults {
} }
@Override @Override
public Failure[] getFailures() { public List<Failure> getFailures() {
return new Failure[0]; return new ArrayList<Failure>();
} }
@Override @Override
...@@ -52,23 +52,16 @@ public class TestResults { ...@@ -52,23 +52,16 @@ public class TestResults {
} }
}; };
private int runCount = 0;
private int runCount; private List<Failure> failures = new ArrayList<Failure>();
private int failureCount;
private Failure[] failures = new Failure[0];
public void add(TestResults results) { public void add(TestResults results) {
this.runCount += results.getRunCount(); this.runCount += results.getRunCount();
this.failureCount += results.getFailureCount(); this.failures.addAll(results.getFailures());
List<Failure> failures = Arrays.asList(this.failures);
failures.addAll(Arrays.asList(results.getFailures()));
this.failures = failures.toArray(new Failure[] {});
} }
public boolean wasSuccessful() { public boolean wasSuccessful() {
return this.failureCount == 0; return this.failures.size() == 0;
} }
public int getRunCount() { public int getRunCount() {
...@@ -80,18 +73,14 @@ public class TestResults { ...@@ -80,18 +73,14 @@ public class TestResults {
} }
public int getFailureCount() { public int getFailureCount() {
return this.failureCount; return this.failures.size();
}
public void setFailureCount(int failureCount) {
this.failureCount = failureCount;
} }
public Failure[] getFailures() { public List<Failure> getFailures() {
return this.failures; return this.failures;
} }
public void setFailures(Failure[] failures) { public void setFailures(List<Failure> failures) {
this.failures = failures; this.failures = failures;
} }
......
...@@ -38,12 +38,16 @@ public class SpockCompilerAutoConfiguration extends CompilerAutoConfiguration { ...@@ -38,12 +38,16 @@ public class SpockCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override @Override
public void applyDependencies(DependencyCustomizer dependencies) public void applyDependencies(DependencyCustomizer dependencies)
throws CompilationFailedException { throws CompilationFailedException {
dependencies.add("spock-core"); dependencies.add("spock-core").add("junit").add("spring-test")
.add("hamcrest-library");
} }
@Override @Override
public void applyImports(ImportCustomizer imports) throws CompilationFailedException { public void applyImports(ImportCustomizer imports) throws CompilationFailedException {
imports.addStarImports("spock.lang"); imports.addStarImports("spock.lang").addStarImports("org.junit")
.addStaticStars("org.junit.Assert")
.addStaticStars("org.hamcrest.MatcherAssert")
.addStaticStars("org.hamcrest.Matchers");
} }
} }
...@@ -25,6 +25,7 @@ import org.springframework.boot.cli.command.TestCommand; ...@@ -25,6 +25,7 @@ import org.springframework.boot.cli.command.TestCommand;
import org.springframework.boot.cli.command.tester.TestResults; import org.springframework.boot.cli.command.tester.TestResults;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
/** /**
...@@ -37,12 +38,11 @@ public class TestCommandIntegrationTests { ...@@ -37,12 +38,11 @@ public class TestCommandIntegrationTests {
@BeforeClass @BeforeClass
public static void cleanGrapes() throws Exception { public static void cleanGrapes() throws Exception {
GrapesCleaner.cleanIfNecessary(); GrapesCleaner.cleanIfNecessary();
// System.setProperty("ivy.message.logger.level", "3");
} }
@Before @Before
public void setup() throws Exception { public void setup() throws Exception {
System.setProperty("disableSpringSnapshotRepos", "true"); System.setProperty("disableSpringSnapshotRepos", "false");
new CleanCommand().run("org.springframework"); new CleanCommand().run("org.springframework");
} }
...@@ -77,9 +77,9 @@ public class TestCommandIntegrationTests { ...@@ -77,9 +77,9 @@ public class TestCommandIntegrationTests {
TestCommand command = new TestCommand(); TestCommand command = new TestCommand();
command.run("samples/nothing.groovy"); command.run("samples/nothing.groovy");
} }
catch (RuntimeException e) { catch (RuntimeException ex) {
assertEquals("Can't find samples/nothing.groovy", e.getMessage()); assertEquals("Can't find samples/nothing.groovy", ex.getMessage());
throw e; throw ex;
} }
} }
...@@ -123,4 +123,14 @@ public class TestCommandIntegrationTests { ...@@ -123,4 +123,14 @@ public class TestCommandIntegrationTests {
assertTrue(results.wasSuccessful()); assertTrue(results.wasSuccessful());
} }
@Test
public void verifyFailures() throws Exception {
TestCommand command = new TestCommand();
command.run("samples/failures.groovy");
TestResults results = command.getResults();
assertEquals(5, results.getRunCount());
assertEquals(3, results.getFailureCount());
assertFalse(results.wasSuccessful());
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment