Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create an argument captor for a Map object in mockito in java?

How to create an argument captor for Map<String, SomeCustomClass>?

I have the code that follows the following pattern:

import java.util.HashMap;
import java.util.Map;

public class CompoundClass {

   public CompoundClass (String a, String b){
       this.a = a;
       this.b = b;
   }

   public String a;
   public String b;
}

public class SubClass {
    public void doSomeThingSubClass(Map<String, CompoundClass> mapSb) {
        ...
    }
}

public class Example {

    public SubClass sb;

    public Example(SubClass sb) {
        this.sb = sb;
    }

    public void doSomeThing () {
        Map<String, CompoundClass> mapSb = new HashMap<>();
        mapSb.put("x", new CompoundClass("aa","bb"));
        sb.doSomeThingSubClass(mapSb);
    }
}

And I want to test if the method doSomethingSubClass(mapSb) was called, whereby I need to be able to check with what argument it was called. For this purpose I have the following unit test:

@Test
void TestDoSomehing(){
    SubClass sb = mock(SubClass.class);
    
    Example ex = new Example(sb);
    
    ArgumentCaptor<Map<String, CompoundClass>> argCaptor = ArgumentCaptor.forClass(Map<String, CompoundClass>.class);

    ex.doSomeThing();

    verify(sb).doSomeThingSubClass(argCaptor.capture());
    
    System.out(argCaptor.getValue().get('x').a);
}

The problem is that the above initialization of the argCaptor produces the following error message: "Cannot select from parametrized type". Therefore, the question is how to declare an initialize in a correct way the argument captor for a map object like Map<String, SomeCustomeClass>? Thanks in advance!

like image 349
Riko Avatar asked Apr 29 '19 14:04

Riko


People also ask

What is the use of ArgumentCaptor class in mockito?

Mockito ArgumentCaptor is used to capture arguments for mocked methods. ArgumentCaptor is used with Mockito verify() methods to get the arguments passed when any method is called. This way, we can provide additional JUnit assertions for our tests.

What is ArgumentCaptor Forclass?

public class ArgumentCaptor<T> extends Object. Use it to capture argument values for further assertions. Mockito verifies argument values in natural java style: by using an equals() method. This is also the recommended way of matching arguments because it makes tests clean & simple.

How do I use argumentcaptor with Mockito?

You can use Mockito's Captor annotation to declare a parameterized instance of ArgumentCaptor. One of the advantages of using @Captor annotation is that you can avoid warnings related capturing complex generic types. Thanks for contributing an answer to Stack Overflow!

What are the methods of argumentcaptor in Java?

There are some important methods of the ArgumentCaptor class that are given below: It is used to capture the method arguments. It is used to build a new ArgumentCaptor. It is used to return all the captured values.

How to create a Mockito captor project in Eclipse?

Open Eclipse. Go to File=>New=>Java Project. In the ‘Project name’ enter ‘MockitoCaptorExample’. Eclipse will create a ‘src’ folder.

What does no argument value was captured mean Mockito?

org.mockito.exceptions.base.MockitoException: No argument value was captured! This exception message is confusing and can trip you to believing that you have issues in your test. However, the issue is in the code you are testing and not in your test.


2 Answers

You can do it either:

with @SuppressWarnings("unchecked")

  @Test
  @SuppressWarnings("unchecked")
  void TestDoSomething(){
    SubClass sb = mock(SubClass.class);

    Example ex = new Example(sb);

    ArgumentCaptor<Map<String, CompoundClass>> argCaptor = ArgumentCaptor.forClass(Map.class);

    ex.doSomeThing();

    verify(sb).doSomeThingSubClass(argCaptor.capture());

    System.out.println(argCaptor.getValue().get("x").a);
  }

or with junit5 and @Captor annotation:

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
@TestInstance(Lifecycle.PER_METHOD)
public class TestDoSomething {

  @Captor
  private ArgumentCaptor<Map<String, CompoundClass>> argCaptor;

  @Test
  void TestDoSomething2(){
    SubClass sb = mock(SubClass.class);

    Example ex = new Example(sb);

    ex.doSomeThing();

    verify(sb).doSomeThingSubClass(argCaptor.capture());

    System.out.println(argCaptor.getValue().get("x").a);
  }
}
like image 57
Matthew I. Avatar answered Nov 01 '22 03:11

Matthew I.


You can use Mockito's Captor annotation to declare a parameterized instance of ArgumentCaptor.

For example, the following test compiles and outputs aa:

@Captor
private ArgumentCaptor<Map<String, CompoundClass>> argCaptor;

@Test
void TestDoSomehing(){
    MockitoAnnotations.initMocks(this);

    SubClass sb = mock(SubClass.class);

    Example ex = new Example(sb);

    ex.doSomeThing();

    verify(sb).doSomeThingSubClass(argCaptor.capture());

    System.out.println(argCaptor.getValue().get("x").a);
}

From the Javadocs:

One of the advantages of using @Captor annotation is that you can avoid warnings related capturing complex generic types.

like image 33
glytching Avatar answered Nov 01 '22 03:11

glytching