2.5. Advanced Groovy features useful to testing

I hope that this chapter serves as a gentle introduction to Groovy, and that if you were scared by the syntax of Spock tests in chapter 1, you’re now more confident about how things work. In several ways, Groovy simplifies Java code by leaving only the gist and discarding the bloat.

Explaining all the things Groovy can do in a single chapter is impossible. Groovy has several advanced constructs for core programming that blow away any Java code you’ve already seen. This last section presents some advanced concepts that you might use in your Spock tests.

Don’t be alarmed if the code shown is more complex than the previous examples. You can skip this part and come back again when you have more experience with Groovy and become comfortable with Spock tests. That being said, the following techniques are in no way essential to Spock tests. They have their uses at times, but you should always make sure that your unit tests aren’t overengineered.

Don’t fall into the trap of using cool Groovy tricks in Spock tests to impress your Java friends! Keep Spock tests simple and understandable.

2.5.1. Using Groovy closures

The official Groovy book (Groovy in Action) assigns a whole chapter to explain closures, so I’m not going to try to do the same in these paragraphs. You might already know closures from other programming languages.[13] If not, spend some time researching them because they’re universally helpful (even outside the context of Groovy).

13

You may have seen function pointers, function references, higher-order methods, and code blocks in other languages. They’re not, strictly speaking, the same thing as closures, but the main concepts are similar.

Closures are in many ways similar to methods. Unlike Java methods, they can be passed around as arguments to other methods or become partially evaluated instead of called directly. Java 8 also comes with lambda expressions, which serve as a stepping stone to functional programming concepts. If you’ve already worked with Java 8, Groovy closures will come naturally to you.

Closures in Groovy are denoted by the -> character and are contained in {}. The following listing presents some examples.

Listing 2.24. Groovy closures

Closures are the Swiss army knife of Groovy. They’re used almost everywhere, and it’s hard to deal with Groovy code without stumbling upon them. Prior to Java 8, they were one of the main advantages of Groovy over Java, and even after Java 8, they still offer great value and simplicity. Closures are so powerful in Groovy that you can use them directly to implement interfaces or as exit points in switch statements.

The Groovy GDK augments the existing JDK with several new methods that accept closures for arguments. For example, a handy Groovy method for unit testing is the every() method available in collections. Assume that you have a Java class that gets a list of image names from a text file and returns only those that end in a specific file extension. Closures can be employed in the Groovy assert, as shown in the next listing.

Listing 2.25. Using Groovy closures in Spock tests

In this Spock test, the assertion is a single line because all elements of the list are checked one by one automatically by the closure. The closure takes as an argument a string and returns true if the string ends in jpg (using both three- and four-letter notations).

Other methods useful to unit tests (apart from every() shown in the preceding listing) are as follows:

  • any(closure)—Returns true if at least one element satisfies closure
  • find(closure)—Finds the first element that satisfies closure
  • findAll(closure)—Finds all elements that satisfy closure

You should consult the Groovy official documentation (www.groovy-lang.org/gdk.html) for more details.

2.5.2. Creating test input with ObjectGraphBuilders

One of the arguments against unit tests (and integration tests, in particular) is the effort required to come up with “real” test data. In a complex business application, the data that’s moved around is rarely a single object. Usually it’s a collection of objects, a tree structure, a graph, or any other complex structure.

This makes writing integration tests a lengthy process because about 80% of the code can be consumed by creating the test input for the class under test. Test input code generation is one of the first candidates for code reuse inside unit tests. In sufficiently large enterprise projects, test input generation might need a separate code module of its own, outside the production code.

Groovy to the rescue! Groovy comes with a set of builders that allow you to create test data by using a fancy DSL. Instead of creating the data manually, you declare the final result. As an example, assume that your domain contains the classes in the following listing.

Listing 2.26. Domain classes in Java

This is a typical business domain. If you look closely enough, you’ll see that it follows certain rules:

  • Each child field has the same name of the class (CargoOrder cargoOrder).
  • Each list is already initialized.
  • Each list field has the plural name of its class (Ship > ships).

Because of these rules, it’s possible to create a deep hierarchy of this domain by using an ObjectGraphBuilder, as shown in the next listing.

Listing 2.27. Using a Groovy builder for quick object creation

This creates a ship registry with three ships, seven people, and four cargo orders, all in about 30 lines of Groovy code. Creating the same tree with Java code would need more than 120 lines of code (for brevity, you can find the code in the source of this book). In this case, Groovy reduces code lines by 75%.

The other important point is the visual overview of the tree structure. Because the ObjectGraphBuilder offers a declarative DSL for the object creation, you can get an overview of the tree structure by looking at the code.

If your domain classes don’t follow the preceding rules, you can either change them (easiest) or inject the ObjectBuilder with custom resolvers to override default behavior. Consult the official Groovy documentation for examples with custom resolvers. By default, the ObjectGraphBuilder will treat as plural (for collections) the class name plus s (ship becomes ships). It also supports special cases with words that end in y (daisy becomes daisies, army becomes armies, and so forth).

2.5.3. Creating test input with Expando

Spock includes comprehensive mocking and stubbing capabilities, as you’ll see in chapter 6. For simple cases, you can also get away with using vanilla Groovy. Groovy shines when it comes to dynamic object creation.

As a final example of Groovy power, I’ll demonstrate how Groovy can create objects on the spot. Assume that you have an interface of this DAO:

public interface AddressDao {
    Address load(Long id);
}

You also have a business service that uses this DAO as follows:

public class Stamper {
    private final AddressDao addressDao;

    public Stamper(AddressDao addressDao)
    {
            this.addressDao = addressDao;
    }

    public boolean isValid(Long addressID)
    {
            Address address = addressDao.load(addressID);
            return address.getStreet()!= null &&
                           address.getPostCode()!= null;
    }
}

This business service checks Address objects (a POJO) and considers them valid if they have both a street and a postal code. You want to write a Spock test for this service. Of course, you could mock the AddressDao, as you’ll see in chapter 6. But with Groovy, you can dynamically create an object that mimics this service, as shown in the following listing.

Listing 2.28. Using Expando to mock interfaces

The magic line here is the one with the as keyword. This keyword performs casting in Groovy but in a much more powerful way than Java. The Expando class has no common inheritance with the AddressDao, yet it can still work as one because of duck typing (both objects have a load() method, and that’s enough for Groovy).

Although this is a common use of Expando classes, they have several other uses that you might find interesting. The combination of duck typing and dynamic object creation will certainly amaze you.[14] The next listing presents another example where I use an Expando for integer generation (which could be used as test data in a Spock test).

14

Just don’t get carried away. Expando overuse is not a healthy habit.

Listing 2.29. Using a Groovy Expando as test-data generator

When you run this code, you’ll get the following:

>groovy ExpandoDemo.groovy
Next number is 0
Next number is 1
Next number is 2
Next number is 3
Reset smart iterator
Next number is 2
Next number is 3

After the iterator is restarted, you can use it again as usual, even though the previous run reached the limit of numbers generated. Notice also that you don’t implement in the Expando class the remove() method defined by the iterator Java interface. The code doesn’t use it, so the Expando object doesn’t need to declare it. But because of duck typing, this Expando still passes as an iterator even though it implements only two out of three required methods.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.172.132