Enterprise Java

Inject a Mock as a Spring Bean in a Spock Spring Test

Spock is a powerful testing framework for Java and Groovy applications, especially for writing unit and integration tests. It integrates seamlessly with the Spring framework. Let us delve to cover how to inject a mock as a Spring bean in a Spock Spring test, including basic test classes and using Spock’s Spring annotations.

1. Introduction

Spock is a testing and specification framework for Java and Groovy applications. It is particularly useful for its expressive language, BDD-style specification, and powerful mocking capabilities. Spring is a comprehensive framework for Java-based enterprise applications, which offers extensive support for dependency injection, aspect-oriented programming, and more.

1.1 Benefits of Spock

  • Expressive Syntax: Spock’s specification language is highly readable and expressive, making test cases easier to understand and maintain.
  • Powerful Mocking: Spock provides built-in support for mocking and stubbing, allowing for flexible and powerful test double creation.
  • Seamless Integration with Groovy: Since Spock is built on Groovy, it leverages Groovy’s concise and flexible syntax.
  • BDD Style Testing: Spock encourages behavior-driven development (BDD) by supporting given-when-then constructs, making test scenarios more descriptive and aligned with business requirements.
  • Comprehensive Testing: Spock supports unit, integration, and functional testing, making it a versatile framework for various testing needs.
  • Extensive Spring Support: Spock integrates smoothly with the Spring framework, facilitating easy testing of Spring beans and configurations.

1.2 Use Cases of Spock

  • Unit Testing: Writing concise and readable unit tests for Java and Groovy applications.
  • Integration Testing: Testing interactions between various components in a Spring application.
  • Behavior-Driven Development (BDD): Defining tests in a business-readable manner to ensure alignment between business requirements and implementation.
  • Mocking and Stubbing: Creating mock objects and defining their behavior for isolated testing of components.
  • Specification by Example: Using examples to specify and verify the behavior of a system, enhancing documentation and understanding of system behavior.
  • Testing Legacy Code: Writing expressive and maintainable tests for legacy codebases to ensure stability during refactoring.

2. Setting Up the Spring Boot Project

First, create a Spring Boot project. You can use Spring Initializr to generate the project with the necessary dependencies: Spring Web, Spring Boot DevTools, Lombok, and Groovy.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.spockframework</groupId>
    <artifactId>spock-spring</artifactId>
    <version>2.0-groovy-3.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy</artifactId>
    <version>3.0.9</version>
</dependency>

3. Creating a Service and Controller

Let’s create a simple service and controller to demonstrate the testing.

3.1 Service

The MyService class is a simple Spring service class annotated with @Service. The @Service annotation is a specialization of the @Component annotation, which indicates that this class is a Spring bean and should be managed by the Spring container.

// File: src/main/java/com/example/demo/MyService.java

package com.example.demo;

import org.springframework.stereotype.Service;

@Service
public class MyService {
    public String greet(String name) {
        return "Hello, " + name;
    }
}

The class contains a single method, greet, which takes a String parameter called name. The purpose of this method is to return a greeting message. Specifically, it concatenates the string “Hello, ” with the provided name parameter and returns the resulting string.

3.2 Controller

The MyController class is a Spring REST controller annotated with @RestController. The @RestController annotation is a combination of the @Controller and @ResponseBody annotations, indicating that this class handles HTTP requests and returns data directly in the response body, rather than a view.

// File: src/main/java/com/example/demo/MyController.java

package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {
    private final MyService myService;

    public MyController(MyService myService) {
        this.myService = myService;
    }

    @GetMapping("/greet")
    public String greet(@RequestParam String name) {
        return myService.greet(name);
    }
}

This controller has a dependency on the MyService class, which is injected via constructor injection. The constructor of the MyController class takes a single parameter of type MyService and assigns it to a private final field named myService. This ensures that the myService dependency is available for use within the controller.

The MyController class defines a single endpoint, /greet, which is mapped using the @GetMapping annotation. This endpoint accepts a name parameter via the @RequestParam annotation. When a GET request is made to the /greet endpoint with a name parameter, the greet method is called. This method delegates to the greet method of the MyService class, passing the name parameter, and returns the resulting greeting message.

4. Writing Spock Tests

Now, we will write tests for our service and controller using Spock. We will inject a mock as a Spring bean to verify the interactions.

4.1 Basic Test Class

The MyServiceTest class is a test class written using the Spock framework, which is a testing and specification framework for Java and Groovy applications. The class extends Specification, which is the base class for Spock specifications and provides a set of powerful features for writing expressive and maintainable tests.

// File: src/test/groovy/com/example/demo/MyServiceTest.groovy

package com.example.demo

import spock.lang.Specification

class MyServiceTest extends Specification {
    def "test greet method"() {
        given:
        MyService myService = new MyService()

        when:
        String result = myService.greet("Spock")

        then:
        result == "Hello, Spock"
    }
}

Within the MyServiceTest class, a single test method named test greet method is defined using Spock’s BDD-style syntax. The method is structured into three main sections: given, when, and then.

  • In the given block, an instance of the MyService class is created. This setup stage initializes the object that will be tested.
  • In the when block, the greet method of the MyService instance is called with the argument “Spock”. The result of this method call is stored in a variable named result.
  • In the then block, an assertion is made to verify that the result of the greet method call is equal to the expected string “Hello, Spock”. This verification stage ensures that the greet method behaves as expected.

4.1.1 Output

To run the tests, execute the mvn test command from the terminal in the project’s root directory. When running the MyServiceTest class, the output log should indicate that the test was executed successfully. Below is the expected output log:

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.example.demo.MyServiceTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.012 s - in com.example.demo.MyServiceTest

Results:

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

4.2 Using Spock’s Spring Annotations

Spock provides several annotations for integrating with the Spring context:

  • @SpringBootTest: Used to create a Spring application context for integration tests.
  • @MockBean: Used to add a mock bean to the Spring application context.
// File: src/test/groovy/com/example/demo/MyControllerTest.groovy

package com.example.demo

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.test.context.ContextConfiguration
import spock.lang.Specification

import static org.mockito.BDDMockito.given

@SpringBootTest
@ContextConfiguration(classes = [MyController])
class MyControllerTest extends Specification {

    @Autowired
    MyController myController

    @MockBean
    MyService myService

    def "test greet endpoint"() {
        given:
        String name = "Spock"
        String greeting = "Hello, Spock"
        given(myService.greet(name)).willReturn(greeting)

        when:
        String result = myController.greet(name)

        then:
        result == greeting
    }
}

4.2.1 Output

To run the tests, execute the mvn test command from the terminal in the project’s root directory. When running the MyControllerTest class, the output log should indicate that the test was executed successfully. Below is the expected output log:

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.example.demo.MyControllerTest
2024-07-19 10:00:00.000  INFO 12345 --- [           main] o.s.t.c.support.AbstractContextLoader    : Using TestContext framework
2024-07-19 10:00:00.000  INFO 12345 --- [           main] o.s.b.t.context.SpringBootTestContextBootstrapper : Found @SpringBootTest annotation
2024-07-19 10:00:00.000  INFO 12345 --- [           main] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 0
2024-07-19 10:00:00.000  INFO 12345 --- [           main] o.s.b.t.context.SpringBootTestContextBootstrapper : Found @MockBean annotation on myService
2024-07-19 10:00:00.000  INFO 12345 --- [           main] o.s.b.t.m.MockDefinitionRegistrar        : Registering mock bean for MyService defined by MyControllerTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.345 s - in com.example.demo.MyControllerTest

Results:

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

5. Conclusion

In this article, we demonstrated how to inject a mock as a Spring bean in a Spock Spring test. We created a simple Spring Boot application, wrote basic test classes, and utilized Spock’s Spring annotations to perform integration tests. This setup helps in writing clean, maintainable, and effective tests for Spring applications.

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button