- In order to add the Spock tests to our application, we will need to make a few changes to our build.gradle file first. As Spock tests are written in Groovy, the first thing to do is add a groovy plugin to our build.gradle file, as follows:
apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'groovy' apply plugin: 'spring-boot'
- We will also need to add the necessary Spock framework dependencies to the build.gradle dependencies block:
dependencies { ... testCompile('org.spockframework:spock-core:1.1-groovy-2.4-rc-2') testCompile('org.spockframework:spock-spring:1.1-groovy-2.4-rc-2') ... }
- As the tests will be in Groovy, we will need to create a new source directory for the files. Let's create the src/test/groovy/com/example/bookpub directory in the root of our project.
- Now we are ready to write our first test. Create a SpockBookRepositorySpecification.groovy file in the src/test/groovy/com/example/bookpub directory at the root of our project with the following content:
package com.example.bookpub;
import com.example.bookpub.entity.Author;
import com.example.bookpub.entity.Book
import com.example.bookpub.entity.Publisher
import com.example.bookpub.repository.BookRepository
import com.example.bookpub.repository.PublisherRepository
import org.mockito.Mockito
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator
import org.springframework.test.web.servlet.MockMvc
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.context.ConfigurableWebApplicationContext
import spock.lang.Specification
import javax.sql.DataSource
import static org.hamcrest.CoreMatchers.containsString
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest @AutoConfigureMockMvc class SpockBookRepositorySpecification extends Specification { @Autowired private ConfigurableWebApplicationContext context @Autowired private DataSource ds; @Autowired private BookRepository repository; @Autowired private MockMvc mockMvc; void setup() { ResourceDatabasePopulator populator =
new ResourceDatabasePopulator(
context.getResource("classpath:/packt-books.sql")); DatabasePopulatorUtils.execute(populator, ds); } @Transactional def "Test RESTful GET"() { when: def result = mockMvc.perform(get("/books/${isbn}")); then: result.andExpect(status().isOk()) result.andExpect( content().string(containsString(title)) ); where: isbn | title "978-1-78398-478-7"|"Orchestrating Docker" "978-1-78528-415-1"|"Spring Boot Recipes" } @Transactional def "Insert another book"() { setup: def existingBook = repository.findBookByIsbn("978-1-78528-415-1") def newBook = new Book("978-1-12345-678-9",
"Some Future Book", existingBook.getAuthor(),
existingBook.getPublisher() ) expect: repository.count() == 3 when: def savedBook = repository.save(newBook) then: repository.count() == 4 savedBook.id > -1 } }
- Execute the tests by running ./gradlew clean test and the tests should get passed.
- As Spock integrates with JUnit, we can see the execution report of the Spock tests together with the rest of our test suite. If we open build/reports/tests/index.html in the browser and click the Classes button, we will see our specification in the table, as shown in the following screenshot:
- Selecting the com.example.bookpub.SpockBookRespositorySpecification link will take us to the detailed report page, which is as follows:
- Next, we will take our tests a bit further and explore the mocking functionality of the database repositories. Let's use PublisherRepository as our candidate to mock, and wire it into the BookController class to provide a getBooksByPublisher functionality. Let's add the following content to the BookController class in the src/main/java/com/example/bookpub/controllers directory at the root of our project:
@Autowired private PublisherRepository publisherRepository; @RequestMapping(value = "/publisher/{id}", method = RequestMethod.GET) public List<Book> getBooksByPublisher(@PathVariable("id") Long id) { Optional<Publisher> publisher =
publisherRepository.findById(id);
Assert.notNull(publisher);
Assert.isTrue(publisher.isPresent());
return publisher.get().getBooks(); }
- Let's add the following to the Publisher class in the src/main/java/com/example/bookpub/entity directory at the root of our project:
@OneToMany(mappedBy = "publisher") @JsonBackReference private List<Book> books;
- Lastly, let's add a getter and setter for the books to the Publisher entity class as well:
public List<Book> getBooks() { return books; } public void setBooks(List<Book> books) { this.books = books; }
- With all the code additions completed, we are ready to add another test to the SpockBookRepositorySpecification.groovy file in the src/test/groovy/com/example/bookpub directory at the root of our project with the following content:
... class SpockBookRepositorySpecification extends Specification { ... @MockBean private PublisherRepository publisherRepository @Transactional def "Test RESTful GET books by publisher"() { setup: Publisher publisher = new Publisher("Strange Books") publisher.setId(999) Book book = new Book("978-1-98765-432-1", "Mystery Book", new Author("John", "Doe"), publisher) publisher.setBooks([book]) Mockito.when(publisherRepository.count()). thenReturn(1L) Mockito.when(publisherRepository.findById(1L)). thenReturn(Optional.of(publisher)) when: def result = mockMvc.perform(get("/books/publisher/1")) then: result.andExpect(status().isOk()) result.andExpect(content(). string(containsString("Strange Books"))) cleanup: Mockito.reset(publisherRepository) } }
- Execute the tests by running ./gradlew clean test and the tests should continue to get passed.