Mastering Spock Unit Tests: From Dependencies to Mocking Static Methods

This article walks through setting up Spock for Java/Groovy projects, lists required Maven dependencies, shows how to handle static‑method mocking with PowerMock and Mockito, and provides concrete code examples for mocking @Autowired components, shared objects, and defining mock behavior.

FunTester
FunTester
FunTester
Mastering Spock Unit Tests: From Dependencies to Mocking Static Methods

Technical Solution Overview

The solution is built on the company‑recommended Spock testing framework, which runs on Groovy and extends JUnit. Spock 2.0 requires Groovy 1.+ and Java compatibility, while its built‑in Mock and Spy cover most scenarios. For static‑method mocking, PowerMock together with Mockito is introduced.

Required Maven Dependencies

<dependency>
    <groupId>org.spockframework</groupId>
    <artifactId>spock-core</artifactId>
    <version>1.2-groovy-2.5</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.spockframework</groupId>
    <artifactId>spock-spring</artifactId>
    <version>1.2-groovy-2.5</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>1.7.4</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>1.7.4</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>2.8.9</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-all</artifactId>
    <version>2.4.7</version>
</dependency>
<dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy</artifactId>
    <version>1.9.9</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.objenesis</groupId>
    <artifactId>objenesis</artifactId>
    <version>3.0.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-core</artifactId>
    <version>2.1</version>
    <scope>test</scope>
</dependency>

Non‑Static Resources and Import Management

Because several test frameworks contain overlapping method names, the article advises against using import static to avoid ambiguous imports. It also notes that the project’s IDE may auto‑import the wrong class, so developers should verify imports manually.

Mocking Objects with Spock and PowerMock

@Autowired Constructor Injection

Example controller:

@Api(tags = "SLA规则管理模块")
@Slf4j
@RestController
@RequestMapping("/hickwall/v1/static/sla")
public class FunController {
    HttpServletRequest request;
    ISlaService service;
    @Autowired
    public FunController(HttpServletRequest request, ISlaService service) {
        this.request = request;
        this.service = service;
    }
}

Spock test:

import com.funtester.service.ISlaService
import com.funtester.vo.sla.SlaBean
import spock.lang.Shared
import spock.lang.Specification
import javax.servlet.http.HttpServletRequest

class FunControllerTest extends Specification {
    def service = Mock(ISlaService)
    @Shared def request = Mock(HttpServletRequest)
    def funController = new FunController(request, service)
}

@Autowired Field Injection (No Constructor)

Example service implementation:

public class ApiImpl implements IApi {
    @Autowired
    private ApiRMapper mapper;
}

Spock test snippet:

import com.funtester.mapper.ApiRMapper
import com.funtester.vo.ApiR
import spock.lang.Shared
import spock.lang.Specification

ApiRMapper mapper = Mock(ApiRMapper)
def drive = new ApiImpl(mapper: mapper)

PowerMock Usage

When a test class is annotated with @RunWith(PowerMockRunner.class), Spock syntax cannot be used directly; PowerMock’s Mock functionality must be employed for static‑method scenarios.

Note: If a bean contains fields annotated with @Autowired, Lombok’s @AllArgsConstructor should be avoided because it can cause startup failures.

Shared Objects and Initialization

Spock’s @Shared annotation allows a single instance to be reused across feature methods. Without it, the object would be recreated for each test.

Demo of shared SlaBean initialization:

@Shared def slaBean = new SlaBean()

def setupSpec() {
    request.getHeader("operator") >> "FunTester"
    slaBean.name = "测试"
    slaBean.quota = 1
    slaBean.upstream = "PRO"
    slaBean.threshold = 0.1
    slaBean.creator = "FunTester"
    slaBean.id = 100
}

Defining Mock Behavior in Spock

Basic when‑then‑expect structure:

def "AddSla"() {
    when:
    def sla = FunController.addSla(slaBean)
    then:
    service.addSla(_) >> { f ->
        assert "FunTester" in f.creator
        1
    }
    expect:
    sla.code == 0
    sla.data == 1
}

Additional examples of return values, exceptions, and delegating to other methods are provided using the _ (any) placeholder.

Mockito + PowerMock Behavior Definition

Mockito is used inside setupSpec to stub static or complex methods before Spock tests run:

Mockito.when(newutil.filter(Mockito.any())).thenReturn(true)
Mockito.when(newutil.filter(Mockito.any())).thenReturn(null)
Mockito.when(newutil.filter(Mockito.any())).thenThrow(Exception.class)
PowerMockito.doNothing().when(newutil).filter(Mockito.any(ArrayList.class))

Returning a list of custom objects:

Mockito.when(newser.selectAllService()).thenReturn([
    new NewInterface() {{
        setUrl("/abc");
        setNname("test");
        setMethod("GET");
    }},
    new NewInterface() {{
        setUrl("/abcd");
        setNname("test");
        setMethod("POST");
    }},
    new NewInterface() {{
        setUrl("/abce");
        setNname("test");
        setMethod("GET");
    }}
])

Conclusion

The article provides a practical, step‑by‑step guide for integrating Spock, PowerMock, and Mockito into a Java/Groovy backend project, covering dependency management, import pitfalls, mocking strategies for both instance and static methods, and shared object initialization. Following these patterns helps achieve high unit‑test coverage while avoiding common compatibility issues.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavatestingunit testingPowerMockMockitoGroovySpock
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.