eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Partner – LambdaTest – NPI EA (cat=Testing)
announcement - icon

Regression testing is an important step in the release process, to ensure that new code doesn't break the existing functionality. As the codebase evolves, we want to run these tests frequently to help catch any issues early on.

The best way to ensure these tests run frequently on an automated basis is, of course, to include them in the CI/CD pipeline. This way, the regression tests will execute automatically whenever we commit code to the repository.

In this tutorial, we'll see how to create regression tests using Selenium, and then include them in our pipeline using GitHub Actions:, to be run on the LambdaTest cloud grid:

>> How to Run Selenium Regression Tests With GitHub Actions

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI (cat= Testing)
announcement - icon

Regression testing is an important step in the release process, to ensure that new code doesn't break the existing functionality. As the codebase evolves, we want to run these tests frequently to help catch any issues early on.

The best way to ensure these tests run frequently on an automated basis is, of course, to include them in the CI/CD pipeline. This way, the regression tests will execute automatically whenever we commit code to the repository.

In this tutorial, we'll see how to create regression tests using Selenium, and then include them in our pipeline using GitHub Actions:, to be run on the LambdaTest cloud grid:

>> How to Run Selenium Regression Tests With GitHub Actions

1. Introduction

When we test our code, we sometimes want to capture the parameters passed to our method.

In this tutorial, we’ll learn how to capture arguments in a Spock test using Stubs, Mocks, and Spies and check what we captured. We’ll also learn how to verify multiple calls to the same Mock with different arguments and assert the order of those calls.

2. The Subject of Our Test

First, we need a method that takes a single parameter or argument we want to capture.

So, let’s create an ArgumentCaptureSubject with a catchMeIfYouCan() method that takes a String and returns it with “Received ” prepended:

public class ArgumentCaptureSubject {
    public String catchMeIfYouCan(String input) {
        return "Received " + input;
    }
}

3. Preparing Our Data-Driven Test

We’ll start our tests with the typical use of a Stub and evolve it to capture arguments.

Let’s create a Stub of our class to return a stubbed response of “42” and invoke its catchMeIfYouCan() method:

def "given a Stub when we invoke it then we capture the stubbed response"() {
    given: "an input and a result"
    def input = "Input"
    def stubbedResponse = "42"

    and: "a Stub for our response"
    @Subject
    ArgumentCaptureSubject stubClass = Stub()
    stubClass.catchMeIfYouCan(_) >> stubbedResponse

    when: "we invoke our Stub's method"
    def result = stubClass.catchMeIfYouCan(input)

    then: "we get our stubbed response"
    result == stubbedResponse
}

We’ve used a simple Stub in this example as we’re not verifying any method invocations.

4. Capturing Arguments

Now we have our basic test, let’s see how to capture the argument we used to invoke our method.

First, we’ll declare a method-scoped variable to assign when we capture the argument:

def captured

Next, we’ll replace our static stubbedResponse with a Groovy Closure. Spock will pass our Closure a List of method arguments when our stubbed method is invoked.

Let’s create a simple Closure to capture the arguments list and assign it to our captured variable:

{ arguments -> captured = arguments }

For our assertion, we’ll assert that the first element in our list of captured arguments, at index 0, equals our input:

captured[0] == input

So, let’s update our test with our captured variable declaration, replace our stubbedResponse with our argument-capturing Closure, and add our assertion:

def "given a Stub when we invoke it then we capture the argument"() {
    given: "an input"
    def input = "Input"

    and: "a variable and a Stub with a Closure to capture our arguments"
    def captured
    @Subject
    ArgumentCaptureSubject stubClass = Stub()
    stubClass.catchMeIfYouCan(_) >> { arguments -> captured = arguments }

    when: "we invoke our method"
    stubClass.catchMeIfYouCan(input)

    then: "we captured the method argument"
    captured[0] == input
}

When we want to return a stubbedResponse as well as capture the arguments, we update our Closure to return it:

{ arguments -> captured = arguments; return stubbedResponse }

...

then: "what we captured matches the input and we got our stubbed response"
captured == input
result == stubbedResponse

Note that although we used “return” for clarity, it isn’t strictly necessary since Groovy closures return the result of the last executed statement by default.

When we’re only interested in capturing one of the arguments, we can capture the argument we want by using its index in the Closure:

{ arguments -> captured = arguments[0] }

...

then: "what we captured matches the input"
captured == input

In this case, our captured variable will be the same type as our parameter – a String.

5. Capturing With Spies

When we want to capture a value but also want the method to continue executing, we add a call to Spy‘s callRealMethod().

Let’s update our test to use a Spy instead of a Stub and use the Spy‘s callRealMethod() in our Closure:

def "given a Spy when we invoke it then we capture the argument and then delegate to the real method"() {
    given: "an input string"
    def input = "Input"

    and: "a variable and a Spy with a Closure to capture the first argument and call the underlying method"
    def captured
    @Subject
    ArgumentCaptureSubject spyClass = Spy()
    spyClass.catchMeIfYouCan(_) >> { arguments -> captured = arguments[0]; callRealMethod() }

    when: "we invoke our method"
    def result = spyClass.catchMeIfYouCan(input)

    then: "what we captured matches the input and our result comes from the real method"
    captured == input
    result == "Received Input"
}

Here, we’ve captured the input argument without impacting the method’s return value.

When we want to change the captured argument before passing it on to the real method, we update it inside the closure, then use Spy’s callRealMethodWithArgs to pass on our updated argument.

So, let’s update our Closure to prepend “Tampered: ” to our String before passing it on to the real method:

spyClass.catchMeIfYouCan(_) >> { arguments -> captured = arguments[0]; callRealMethodWithArgs('Tampered:' + captured) }

And let’s update our assertion to expect our tampered result:

result == "Received Tampered:Input"

6. Capturing Arguments Using an Injected Mock

Now that we’ve seen how to use Spock’s mocking framework to capture an argument, let’s apply this technique to a class with a dependency that we can mock.

First, let’s create an ArgumentCaptureDependency class that our subject can call with a simple catchMe() method that takes and modifies a String:

public class ArgumentCaptureDependency {
    public String catchMe(String input) {
        return "***" + input + "***";
    }
}

Now, let’s update our ArgumentCaptureSubject with a constructor that takes our ArgumentCaptureDependency. Let’s also add a callOtherClass method that takes no parameters and calls our ArgumentCaptureDependency‘s catchMe() method with a parameter:

public class ArgumentCaptureSubject {
    ArgumentCaptureDependency calledClass;

    public ArgumentCaptureSubject(ArgumentCaptureDependency calledClass) {
        this.calledClass = calledClass;
    }

    public String callOtherClass() {
        return calledClass.catchMe("Internal Parameter");
    }
}

Finally, let’s create a test like before. This time, let’s inject a Spy into our ArgumentCaptureSubject when we create it so that we can also callRealMethod() and compare the result:

def "given an internal method call when we invoke our subject then we capture the internal argument and return the result of the real method"() {
    given: "a mock and a variable for our captured argument"
    ArgumentCaptureDependency spyClass = Spy()
    def captured
    spyClass.catchMe(_) >> { arguments -> captured = arguments[0]; callRealMethod() }

    and: "our subject with an injected Spy"
    @Subject argumentCaptureSubject = new ArgumentCaptureSubject(spyClass)

    when: "we invoke our method"
    def result = argumentCaptureSubject.callOtherClass(input)

    then: "what we captured matches the internal method argument"
    captured == "Internal Parameter"
    result == "***Internal Parameter***"
}

Our test captured the internal argument “Internal Parameter“. In addition, our invocation of Spy‘s callRealMethod ensured that we didn’t impact the result of the method: “***Internal Parameter***”.

When we don’t need to return the real result, we can simply use a Stub or Mock.

Note that when we test Spring applications, we can inject our Mock using Spock’s @SpringBean annotation.

7. Capturing Arguments From Multiple Invocations

Sometimes, our code invokes a method multiple times, and we want to capture the value for each invocation.

So, let’s add a String parameter to the callOtherClass() method in our ArgumentCaptureSubject. We’ll invoke this with different parameters and capture them.

public String callOtherClass(String input) {
    return calledClass.catchMe(input);
}

We need a collection to capture the argument from each invocation. So, we’ll declare a capturedStrings variable as an ArrayList:

def capturedStrings = new ArrayList()

Now, let’s create our test and make it invoke our callOtherClass() twice, first with “First” as a parameter and then with “Second”:

def "given an dynamic Mock when we invoke our subject then we capture the argument for each invocation"() {
    given: "a variable for our captured arguments and a mock to capture them"
    def capturedStrings = new ArrayList()
    ArgumentCaptureDependency mockClass = Mock()

    and: "our subject"
    @Subject argumentCaptureSubject = new ArgumentCaptureSubject(mockClass)

    when: "we invoke our method"
    argumentCaptureSubject.callOtherClass("First")
    argumentCaptureSubject.callOtherClass("Second")
}

Now, let’s add a Closure to our Mock to capture the argument from each invocation and add it to our list. Let’s also have our Mock verify that our method was called twice by prefixing “2 *” to our statement: 

then: "our method was called twice and captured the argument"
2 * mockClass.catchMe(_ as String) >> { arguments -> capturedStrings.add(arguments[0]) }

Finally, let’s assert that we captured both arguments in the right order:

and: "we captured the list and it contains an entry for both of our input values"
capturedStrings[0] == "First"
capturedStrings[1] == "Second"

When we’re not concerned about the order, we can use List‘s contains method:

capturedStrings.contains("First")

8. Using Multiple Then Blocks

Sometimes, we want to assert a sequence of calls using the same method with different arguments but don’t need to capture them. Spock allows verifications in any order within the same then block, so it won’t matter what order we write them in. We can, however, enforce the order by adding multiple then blocks.

Spock verifies that assertions in one then block are met before assertions in the next then block.

So, let’s add two then blocks to verify that our method is invoked with the correct arguments in the right sequence:

def "given a Mock when we invoke our subject twice then our Mock verifies the sequence"() {
    given: "a mock"
    ArgumentCaptureDependency mockClass = Mock()

    and: "our subject"
    @Subject argumentCaptureSubject = new ArgumentCaptureSubject(mockClass)

    when: "we invoke our method"
    argumentCaptureSubject.callOtherClass("First")
    argumentCaptureSubject.callOtherClass("Second")

    then: "we invoked our Mock with 'First' the first time"
    1 * mockClass.catchMe( "First")

    then: "we invoked our Mock with 'Second' the next time"
    1 * mockClass.catchMe( "Second")
}

When our invocations occur in the wrong order, such as when we invoke callOtherClass(“Second”) first, Spock gives us a helpful message:

Wrong invocation order for:

1 * mockClass.catchMe( "First")   (1 invocation)

Last invocation: mockClass.catchMe('First')

Previous invocation:
	mockClass.catchMe('Second')

9. Conclusion

In this tutorial, we learned how to capture method arguments using Spock’s Stubs, Mocks, and Spies using Closures. Next, we learned how to use a Spy to change a captured argument before invoking the real method. We also learned how to collect arguments when our method is invoked multiple times. Finally, as an alternative to capturing the argument, we learned how to use multiple then blocks to check that our invocations occurred in the right sequence.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)