With the def mystery solved, the second striking feature of Spock tests is the lack of assert statements for evaluating results. All JUnit tests end with one or more assert statements6 that define the expected result of the computation. If the expected result doesn’t match the actual one, the test will fail. JUnit comes with an extensive API for assert statements, and it’s considered a good practice to create your own extensions, dealing with the business domain of your application.
I mentioned in the previous chapter that unlike JUnit, Spock doesn’t have assert methods. In this section, you’ll see how Spock deals with assertions and how they can help you in case of test failures. I’ll also continue with the general theme of this chap- ter: reducing the amount of code needed when using Groovy instead of Java.
2.2.1 Understanding how Groovy handles asserts
In theory, Groovy asserts function in a similar way to Java asserts. They expect a Bool- ean variable (or an expression that evaluates to a Boolean), evaluate it, and if it’s true,
6 Not having an assert (or verify) statement is a huge antipattern, because the test never fails.
Java
Java-like Groovy
Concise Groovy
Idiomatic Groovy
Figure 2.2 Suggested path for better Spock tests
the assertion passes successfully. Spock runs assertions in the same way. If all assertions pass, the unit test succeeds.
In practice, however, Java is very strict regarding true/false. Only Boolean vari- ables can be tested for assertions. Groovy takes a more relaxed7 approach to this, allowing all objects to be treated as Booleans.
Groovy treats all objects8 as true unless
■ The object is an empty string.
■ The object is a null reference.
■ The object is the zero number.
■ The object is an empty collection (map, list, array, and so on).
■ The object is the false Boolean (obviously).
■ The object is a regex matcher that fails.
The following listing shows some examples, with Groovy assertions demonstrating the rules of Groovy true/false.
assert true assert !false
assert true || false assert true && !false String firstName = "Susan"
assert firstName def lastName = "Ivanova"
assert lastName String empty = ""
assert !empty Person person = new Person() assert person;
Person nullReference = null
assert !nullReference;
int answerToEverything = 42 assert answerToEverything
int zero=0 assert !zero
Object[] array= new Object[3];
assert array
7 Or error-prone, if you wish. Some of the old C traps are now possible with Groovy as well (but not all).
8 Closures are also “true.”
Listing 2.9 Groovy can convert everything to a Boolean
Boolean variables work like Java.
A nonempty string is true.
An empty string is false.
A valid reference is true.
A null reference is false.
A nonzero number is true.
A zero number is false.
A nonempty collection is true.
43 Groovy Power assert as a replacement for JUnit asserts
Object[] emptyArray= new Object[0];
assert !emptyArray
Pattern myRegex = ~/needle/
assert myRegex.matcher("needle in haystack") assert !myRegex.matcher("Wrong haystack")
println "Script has finished because all asserts pass"
If you run the preceding example, all asserts evaluate to true, and the final line is printed in the console.
GROOVY TRUTH
The way Groovy handles true/false statements (called Groovy truth in Groovy par- lance) can be used in Spock to trim the assert statement into a shorter form instead of converting it explicitly to Boolean variables.
The next listing presents a Spock example with both approaches, using both an explicit Boolean evaluation (Java) and automatic “casting” to true/false (Groovy).
The class under test is a trivial string tokenizer that counts word occurrences.
class GroovyTruthSpec extends spock.lang.Specification{
def "demo for Groovy truth"() {
when: "a line of text is processed"
WordDetector wordDetector = new WordDetector();
wordDetector.parseText("Understanding is a three edged sword:
your side, their side, and the truth");
then: "word frequency should be correct"
wordDetector.wordsFound() > 0 wordDetector.duplicatesFound().size() > 0
wordDetector.wordsFound()
wordDetector.duplicatesFound() }
}
As an exercise, locate examples in chapter 1 that don’t use Groovy truth rules in the assert statements and rewrite them now that you know that Groovy can convert every- thing to a Boolean variable.9
Fun with Groovy truth
This is valid Groovy code: boolean flag = -45. Even though this line doesn’t even compile in Java, in Groovy the number –45 is a nonzero number, and therefore the variable flag is now true.
Listing 2.10 Groovy truth used in Spock tests
9 Groovy strings also get an additional toBoolean() method that treats only true, y, and 1 as true. An empty collection is false.
Creation of regular expression
Regex is true if it matches at least once.
Class under test is a Java class.
Calling a Java method
Using Java-like asserts with explicit conversion to Boolean Any positive number is
automatically seen as true.
Any nonempty collection is automatically seen as true.
2.2.2 Using Groovy assertions in Spock tests
In the previous section, you saw how to use Groovy truth to simplify your assert state- ments. I admit that this is another feature that looks mainly like sugarcoating, and you might not be impressed by the amount of code reduced. This is understandable, but the advantage of Groovy assertions isn’t the application of Groovy truth rules.
The killer feature of Groovy (and therefore Spock) is the information it gives when an assert fails. You’ve seen some hints of this in chapter 1, using assertions that expect numbers (code listings 1.2 and 1.3). In complex expressions, Groovy shows all inter- mediate results. Figure 2.3 shows the Eclipse JUnit window in both cases, but you get similar output if you run your unit tests in the command line or any other compatible tool with JUnit.
The magic of this feature is that it works with all objects and not just primitives.
Groovy has no such distinction: everything is an object as far as Groovy is concerned.
What == means in Groovy
In Groovy, the == operator isn’t testing identity like Java. It calls the equals() method of the object. Identity in Groovy is handled by the is keyword. Thus object1.is(object2) is the Groovy way of testing identity. You should be aware of this difference if you use objects in both sides of the assert statement. (If you perform only simple assertions with scalar values—as you should—then this difference doesn’t matter.)
JUnit asset
Groovy assert
Figure 2.3 Groovy assert shows much more information than a JUnit assert.
45 Groovy Power assert as a replacement for JUnit asserts
Figure 2.4 is a more complex example of a failed Groovy assert with lists. Again notice how Groovy shows all intermediate operations, whereas JUnit reports only the final result.
A Groovy Power assert works for your own objects as well, as shown in figure 2.5.
This Spock feature is crucial for continuous delivery environments. As a developer, you can understand exactly what goes wrong when a test fails. A well-configured build server keeps all the results from unit tests and provides reports for the failed ones.
JUnit asset
Groovy assert
Figure 2.4 Groovy assert with lists compared to JUnit assert
JUnit asset
Groovy assert
Figure 2.5 Groovy assert with the Java class shown in listing 2.10
Because Groovy (and by extension Spock) shows you the running context, you can, in several cases, fix the bug right away instead of spending time with a debugger in order to reproduce it. For enterprise environments in which running an integration test is a lengthy process, this extra context for failing tests is a time-saver that can easily per- suade any Java developer to switch from JUnit.
I’ll show you how to further enhance the output of Groovy Power asserts in chapter 4. For now, I’ll continue with some useful Groovy features that have helped me in sev- eral Spock tests.