How it works...

As you saw from this example, writing tests can be just as elaborate and sophisticated as the production code being tested itself. Let's examine the steps that we took in order to get the Spock tests integrated into our Spring Boot application.

The first thing that we did was to add a Groovy plugin in order to make our build Groovy-friendly, and we also added the required Spock library dependencies of spock-core and spock-spring, both of which are required to make Spock work with Spring's dependency injection and contexts.

The next step was to create the SpockBookRepositorySpecification Spock specification, which extends the Spock's specification abstract base class. Extending the Specification class is very important because this is how JUnit knows that our class is the test class that needs to be executed. If we look in the Specification source, we will see the @RunWith(Sputnik.class) annotation, just like the one that we used in the Cucumber recipe. In addition to the JUnit bootstrapping, the Specification class provides us with many helpful methods and mocking support as well.

For more information about the detailed capabilities that are offered by Spock, you can refer to the Spock documentation that is available at http://spockframework.github.io/spock/docs/current/index.html.

It is also worth mentioning that we used the same annotations for the SpockBookRepositorySpecification class as we did for our Spring Boot-based tests, as shown in the following code:

@SpringBootTest
@AutoConfigureMockMvc

The reason that we had to add @AutoConfigureMockMvc in addition to @SpringBootTest is to add functionality to allow us to use the @Autowire MockMvc instance instead of having to create one ourselves. Regular @SpringBootTest does not automatically create and configure an instance of a MockMvc object, so we could have either created it manually, as we did in BookPubApplicationTests, or added the @AutoConfigureMockMvc annotation, which is what gets used inside @WebMvcTest, to let Spring handle it for us. The good news is that we can always use the same annotation compositions as used by Spring Boot, and annotate our classes directly, which is exactly what we did.

Unlike Cucumber, Spock combines all the aspects of the test in one Specification class, dividing it into multiple blocks, as follows:

  • setup: This block is used to configure the specific test with variables, populating data, building mocks, and so on.
  • expect: This block is one of the stimulus blocks, as Spock defines it, designed to contain simple expressions asserting a state or condition. Besides evaluating the conditions, we can only define variables in this block, and nothing else is allowed.
  • when: This block is another stimulus type block, which always goes together with then. It can contain any arbitrary code and is designed to define the behavior that we are trying to test.
  • then: This block is a response type block. It is similar to expect and can only contain conditions, exception checking, variable definition, and object interactions, such as how many times a particular method has been called and so forth.
More information on interaction testing is available on Spock's website at http://spockframework.github.io/spock/docs/current/interaction_based_testing.html.
  • cleanup: This block is used to clean the state of the environment and potentially undo whatever changes were done as part of the individual test execution. In our recipe, this is where we will reset our PublisherRepository mock object.

Spock provides us with the instance-based setup() and cleanup() methods as well, which can be used to define the setup and cleanup behavior that is common to all the tests in the specification.

If we look at our setup() method, this is where we can configure the database population with the test data. An interesting and important nuance is that the setup() method gets executed before every test method, not once per class. It is important to keep that in mind when doing things like populating a database to avoid re-insertion of the same data multiple times without proper rollback.

To help us with that is the @Transactional annotation of the test methods. Just like the @txn tag in the Cucumber feature files, this annotation instructs Spock to execute the annotated method and its corresponding setup() and cleanup() executions with a transaction scope, which get rolled back after the particular test method is finished. We rely on this behavior to get a clean database state for every test, so we don't end up inserting duplicate data during the execution of the setup() method every time each of our tests runs.

Most of you are probably wondering why we had to add the @JsonBackReference annotation to our Publisher entity class. The answer has to do with the Jackson JSON parser and how it handles circular dependency. In our model, we have a book belonging to a publisher and each publisher has multiple books. When we created our Publisher class with the Books mock and assigned a publisher instance to a book—which later got put in the publisher's book collection—we created a circular reference. During the execution of the BookController.getBooksByPublisher(...) method, the Jackson renderer would have thrown StackOverflowError while trying to write the object model to JSON. By adding this annotation to Publisher, we told Jackson how the objects reference each other, so instead of trying to write out the complete object tree, Jackson now handles it correctly, thus avoiding the circular reference loop situation.

The last thing that is important to keep in mind is how Spring Boot handles and processes the repository interfaces that are annotated with @RepositoryRestResource. Unlike the BookRepository interface, which we have annotated with a plain @Repository annotation and later explicitly declared as an autowire dependency of our BookController class, we did not create an explicit controller to handle RESTful requests for the rest of our repository interfaces such as PublisherRepository and others. These interfaces get scanned by Spring Boot and automatically wrapped with the mapped endpoints that trap the requests and delegate the calls to the backing SimpleJpaRepository proxy. Due to this setup, we can use only the mock object replacement approach for these objects that have been explicitly injected as bean dependencies such as with our example of BookRepository. The good news is that in these situations, where we don't explicitly expect beans to be wired and only use some annotations to stereotype the interfaces for Spring Boot to do its magic, we can rely on Spring Boot to do the job correctly. We know that it has tested all the functionalities behind it so that we don't have to test them. To test the actual repository and entity functionality, we can use the @DataJpaTest annotation to do a specific JPA slice test instead.

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

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