Converting requirements to Spock tests

Một phần của tài liệu Manning java testing with spock (Trang 130 - 134)

Spock blocks embody the low-level mechanics of unit tests. You should also pay equal attention to the methods and classes that contain them. In large enterprise projects, organization and naming of unit tests play a crucial role in easy maintenance and effortless refactoring.

Spock also offers metadata that you can use to annotate your tests for extra clarity.

The advantage that this metadata has over normal Java comments is that it can be extracted by reporting tools.

4.2.1 Explaining the feature examined in a Spock test

A unique characteristic of Spock test methods is the capability to name them by using full English sentences. This is a huge advantage for Spock tests because it makes read- ing tests so much easier (even for nontechnical colleagues).

Listing 4.14 Using cleanup: to release resources even if test fails

The where: block is shown in chapter 5

If you’ve been paying close attention, you must have noticed that I haven’t said any- thing about the where: block. The where: block is used exclusively for parameterized tests. There are so many things to discuss about parameterized Spock testing that it has its own chapter. Chapter 5 covers parameterized tests and the possible forms of the where: block, so keep reading to get the full picture on all Spock blocks.

cleanup: block will always run, even if then: fails.

then: block examines the final result.

I’ve used this technique since the first chapter and consider it a groundbreaking fea- ture of Spock, compared to the status quo. The following listing provides an example.

def "A basket with one product has equal weight"() { given: "an empty basket and a TV"

Product tv = new Product(name:"bravia",price:1200,weight:18) Basket basket = new Basket()

when: "user wants to buy the TV"

basket.addProduct(tv)

then: "basket weight is equal to the TV"

basket.currentWeight == tv.weight }

It’s your job to make sure that this text is understandable (even out of context). Ide- ally, it should match the specifications given by business analysts. If you don’t have detailed specifications (and you should), the method name should describe what’s being tested in a nontechnical way.

The names of Spock methods will appear in test results and coverage reports, so always assume that somebody will read this text without having direct access to the code of the implementation.

4.2.2 Marking the class under test inside a Spock test

In most unit tests, initialization code prepares multiple classes and input data. The class that will be tested and evaluated has more importance than its collaborators, which are the classes that communicate with it, but not under test (either because they have their own tests or because they’re assumed to be correct).

To distinguish this special class, Spock offers the @Subject annotation, as shown is the next listing. In this example, the class under test is the Basket class.

def "A basket with two products weights as their sum (better)"() { given: "an empty basket"

@Subject

Basket basket = new Basket() and: "several products"

Product tv = new Product(name:"bravia",price:1200,weight:18) Product camera = new Product(name:"panasonic",price:350,weight:2) when: "user wants to buy the TV and the camera and the hifi"

basket.addProduct tv basket.addProduct camera

then: "basket weight is equal to all product weight"

basket.currentWeight == (tv.weight + camera.weight) }

Listing 4.15 Test method describes exactly what is being tested

Listing 4.16 Marking the class under test

Full English text

The subject of this test is the Basket class.

107 Converting requirements to Spock tests

In this simple example, it might be easy to understand that the Basket class is the one being tested (especially by looking at the when: and then: blocks), but in larger unit tests, the class under test might not be obvious.

At the time of writing, there’s no reporting tool that takes into account the @Sub- ject annotation, but you should use it anyway to improve readability by other pro- grammers. In current reporting tools, you can’t see which class is under test and you’re forced to look at the source code to identify it. Hopefully, this limitation will be amended soon by newer versions of test reporting tools.

4.2.3 Describing the Spock unit test as a whole

You now have multiple test methods (features in Spock terminology) and want to group them in a Groovy class. This class is a specification, as you can see in the following listing.

@Title("Unit test for basket weight") class BasketWeightSpec extends spock.lang.Specification{

[...test methods here redacted for brevity...]

}

The class that contains all the test methods is a Groovy class that must extend spock.lang.Specification. This makes it a Spock test. The name of the class can be anything, but it’s good practice to end the name in Spec (for example, BasketWeight- Spec). You can pick any ending you want, as long as it’s the same on all your Spock tests, because it makes it easier for the build system (e.g., Maven) to detect Spock tests.

For technical reasons, Spock can’t allow you to name the class with full English text like the test methods. To remedy this limitation, it instead offers the @Title annota- tion, which you can use to give a human-readable explanation of the features that make up this specification.

As an extra bonus, Spock also offers the @Narrative annotation, which can provide even more text that describes what the test does, as shown in the following listing.

Listing 4.17 Writing a Spock specification

Naming .groovy files using the expected Java convention

Unlike Java, Groovy doesn’t require the name of the class and the name of the file on the disk to be the same. You can place the BasketWeightSpec class in a file called MyBasketWeightUnitTest.groovy if that’s what you want. For simplicity, I still urge you to use the Java convention because it makes navigating Spock tests much easier.

Therefore, the BasketWeightSpec class should be placed in a file named Basket- WeightedSpec.groovy.

Description in full English sentence

Groovy class extends Specification.

@Narrative(""" A empty basket starts with no weight. Adding products to the basket increases its weight. The weight is then used for billing during shipping calculations.

Electronic goods have always zero weight.

""")

@Title("Unit test for basket weight")

@Subject(Basket) class BasketWeightDetailedSpec extends spock.lang.Specification{

[...test methods here redacted for brevity...]

}

This listing uses a Groovy multiline string that allows you to insert as many lines of text as you want (a feature that your business analysts might love). In Groovy, multiline strings need triple quotes.

Listing 4.18 also shows the application of the @Subject annotation on the whole class. If you find that all your test methods focus on the same class (which is the usual case), you can apply the @Subject annotation at the top of the Spock test instead of placing it multiple times in the test methods. The class under test is then used as an argument (no need to add the .class extension).

Notice that for brevity I omit the @Title and @Narrative annotations (and usually

@Subject as well) in this book’s examples. You should always attempt to include them in your Spock tests. I tend to look at @Title and @Subject as a compulsory requirement for a Spock test. @Narrative is good to have, but not essential for all kinds of tests.

4.2.4 Revising our view of a Spock test

Because I started explaining Spock elements by using a bottom-up approach, now that we’ve reached the top, let’s see how to revise all parts of a Spock test, as shown in figure 4.12.

Listing 4.18 Writing a full Spock specification

Longer description of unit test Groovy

multiline string Single-line

description of test

Information on the class under test for all test methods

A Spock specification is a Groovy class that contains one or more features (e.g., BasketWeightDetailedSpec).

Each feature (test method) has Spock blocks; for example, the basic given:, when:, and then: blocks to test basket and product weights.

A feature is a test method.

18 kg 18 kg

==

given: when: then:

Figure 4.12 Blocks inside features (test methods) inside Specification (Groovy class)

109 Exploring the lifecycle of a Spock test

A Spock test is a Groovy class that extends spock.lang.Specification. It should be marked with the @Title annotation to explain its purpose.

The Spock test contains one or more test methods (features in Spock terminology) that examine various aspects of the class under test. Test methods can be named directly with full English sentences. The class under test should be marked with the @Subject annotation, either in each test method individually or at the top of the specification.

Finally, each Spock feature (test method) is characterized by the Spock blocks it contains. The most basic structure is the given-when-then blocks that prepare the test, trigger the tested action, and examine the results.

This diagram is useful for our next topic: the lifecycle of a Spock specification.

Một phần của tài liệu Manning java testing with spock (Trang 130 - 134)

Tải bản đầy đủ (PDF)

(306 trang)