Mockito 3 Argument Captor

The method takes input or arguments which governs the behavior of the method. Mockito by default validates the arguments using java defined way ie with the help of equals method, which is also a recommended way of doing, it keeps tests clean and simple.

Mockito provides us a way to capture arguments passed in the test cases, which can further use to validate and asserts. Argument Captor can be created in 2 ways.

  • @Captor
  • ArgumentCaptor#forClass

ArgumentCaptor#forClass

package com.example.mokito3.sujan;

public class AppleService {
    String saveApple(String apple) {
        String appleString = "i love " + apple + " apple";
        return appleString;
    }
}
package com.example.mokito3.sujan;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class AppleServiceTest {
    @Mock
    private AppleService appleService;

    @Test
    void saveAppleMockTest() {
        when(appleService.saveApple("Macintosh")).thenReturn("i eat apple");
        appleService.saveApple("Macintosh");
        ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
        verify(appleService).saveApple(argumentCaptor.capture());
        System.out.println("argumentCaptor = " + argumentCaptor.getValue());
        assertEquals("Macintosh", argumentCaptor.getValue());
    }
}
plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories { jcenter() }
dependencies {
    testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
    testCompile 'org.mockito:mockito-junit-jupiter:3.4.4'
}
test {
    useJUnitPlatform()
}

 

@Captor

@Captor annotation provides a easy and clean way to create Argument Captor.

package com.example.mokito3.sujan;

public abstract class AppleService {
    String saveApple(String apple) {
        String appleString = "i love " + apple + " apple";
        return appleString;
    }
}
package com.example.mokito3.sujan;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class AppleServiceTest {
    @Captor
    ArgumentCaptor<String> argumentCaptor;
    @Mock
    private AppleService appleService;

    @Test
    void saveAppleMockTest() {
        when(appleService.saveApple("Macintosh")).thenReturn("i eat apple");
        appleService.saveApple("Macintosh");
        ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
        verify(appleService).saveApple(argumentCaptor.capture());
        System.out.println("argumentCaptor = " + argumentCaptor.getValue());
        assertEquals("Macintosh", argumentCaptor.getValue());
    }
}
plugins {
    id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'

repositories { jcenter() }
dependencies {
    testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
    testCompile 'org.mockito:mockito-junit-jupiter:3.4.4'
}
test {
    useJUnitPlatform()
}

 

Argument Captor for User Defined Class

Argument capture is not only limited to java defined classes, but it can also be used to capture arguments for classes created by developers as well.

package com.example.mokito3.sujan;

import java.util.Objects;

public class Apple {
    private int appleNo;
    private String appleName;

    public Apple(int appleNo, String appleName) {
        this.appleNo = appleNo;
        this.appleName = appleName;
    }

    public int getAppleNo() {
        return appleNo;
    }

    public String getAppleName() {
        return appleName;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Apple apple = (Apple) o;
        return appleNo == apple.appleNo &&
                Objects.equals(appleName, apple.appleName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(appleNo, appleName);
    }

    @Override
    public String toString() {
        return "Apple{" +
                "appleNo=" + appleNo +
                ", appleName='" + appleName + '\'' +
                '}';
    }
}
package com.example.mokito3.sujan;

public abstract class AppleService {
    String saveApple(Apple apple) {
        String appleString = "apple No is:- " + apple.getAppleNo() + " apple Name is:- " + apple.getAppleName();
        return appleString;
    }
}
package com.example.mokito3.sujan;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class AppleServiceTest {
    @Mock
    private AppleService appleService;

    @Test
    void saveAppleMockTest() {
        Apple apple=new Apple(1, "Macintosh");
        when(appleService.saveApple(apple)).thenReturn("i eat apple");
        appleService.saveApple(apple);
        ArgumentCaptor<Apple> argumentCaptor = ArgumentCaptor.forClass(Apple.class);
        verify(appleService).saveApple(argumentCaptor.capture());
        System.out.println("argumentCaptor = " + argumentCaptor.getValue());
        assertEquals(apple, argumentCaptor.getValue());
    }
}
plugins {
    id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'

repositories { jcenter() }
dependencies {
    testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
    testCompile 'org.mockito:mockito-junit-jupiter:3.4.4'
}
test {
    useJUnitPlatform()
}

 

Argument Captor for Multiple Calls

We call a method multiple times with different arguments, if we need to capture all the arguments in order to further assert them, Mockito provides us ArgumentCaptor#getAllValues() method. It will capture all the arguments passed as input to the mock method.

package com.example.mokito3.sujan;

public abstract class AppleService {
    String saveApple(String apple) {
        String appleString = "i love " + apple + " apple";
        return appleString;
    }
}
package com.example.mokito3.sujan;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Arrays;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class AppleServiceTest {
    @Mock
    private AppleService appleService;

    @Test
    void saveAppleMockTest() {
        when(appleService.saveApple("Macintosh")).thenReturn("i eat apple");
        appleService.saveApple("Macintosh");
        appleService.saveApple("Fuji");
        appleService.saveApple("Gala");
        ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
        verify(appleService, times(3)).saveApple(argumentCaptor.capture());
        System.out.println("argumentCaptor = " + argumentCaptor.getAllValues());
        assertEquals(Arrays.asList("Macintosh", "Fuji", "Gala"), argumentCaptor.getAllValues());
    }
}
plugins {
    id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'

repositories { jcenter() }
dependencies {
    testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
    testCompile 'org.mockito:mockito-junit-jupiter:3.4.4'
}
test {
    useJUnitPlatform()
}

 

Argument Captor for Variable Arguments or Varargs

Methods can take a single input, list of input, or array since java 1. In java 5, a new way of passing array was introduced, Variable Arguments or in short Varargs. Mockito Argument Captor can be used to capture Varargs arguments as well.

package com.example.mokito3.sujan;

public abstract class AppleService {
    String saveApple(String... apple) {
        String appleString = "i love " + apple + " apple";
        return appleString;
    }
}
package com.example.mokito3.sujan;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Arrays;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class AppleServiceTest {
    @Mock
    private AppleService appleService;

    @Test
    void saveAppleMockTest() {
        when(appleService.saveApple("Macintosh", "Fuji", "Gala")).thenReturn("i eat apple");
        appleService.saveApple("Macintosh", "Fuji", "Gala");
        ArgumentCaptor<String[]> argumentCaptor = ArgumentCaptor.forClass(String[].class);
        verify(appleService).saveApple(argumentCaptor.capture());
        System.out.println("argumentCaptor = " + argumentCaptor.getAllValues());
        assertEquals(Arrays.asList("Macintosh", "Fuji", "Gala"), argumentCaptor.getAllValues());
    }
}
plugins {
    id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'

repositories { jcenter() }
dependencies {
    testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
    testCompile 'org.mockito:mockito-junit-jupiter:3.4.4'
}
test {
    useJUnitPlatform()
}

 

Argument Captor with stubbing

ArgumentCapture is a suitable way to capture the argument passed as a parameter so that it can be used further for assertions, but ArgumentCaptor should never be used with stubbing because if stub never called then ArgumentCapture will be null and test case might throw NullPointerException.

ArgumentCaptor should never be used with stubbing

package com.example.mokito3.sujan;

public class AppleService {
    String saveApple(String apple) {
        String appleString = "i love " + apple + " apple";
        return appleString;
    }
}
package com.example.mokito3.sujan;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class AppleServiceTest {
    @Captor
    ArgumentCaptor<String> argumentCaptor;
    @Mock
    private AppleService appleService;

    @Test
    void saveAppleMethodMockTest() {
        /* never use argumentCaptor while stubbing */
        when(appleService.saveApple("Macintosh")).thenReturn("i eat apple");
        /* stub never invoked
        appleService.saveApple("Macintosh");*/
        ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
        verify(appleService).saveApple(argumentCaptor.capture());
        System.out.println("argumentCaptor = " + argumentCaptor.getValue());
        assertEquals("Macintosh", argumentCaptor.getValue());
    }

    @Test
    void saveAppleAnnotationMockTest() {
        /* never use argumentCaptor while stubbing */
        when(appleService.saveApple(argumentCaptor.capture())).thenReturn("i eat apple");
        /* stub never invoked
        appleService.saveApple("Macintosh");*/
        verify(appleService).saveApple(argumentCaptor.capture());
        System.out.println("argumentCaptor = " + argumentCaptor.getValue());
        assertEquals("Macintosh", argumentCaptor.getValue());
    }
}
plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories { jcenter() }
dependencies {
    testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
    testCompile 'org.mockito:mockito-junit-jupiter:3.4.4'
}
test {
    useJUnitPlatform()
}

follow us on