user1184100
user1184100

Reputation: 6894

How to get elements from Map when it is mocked

I'm unable to verify whether object exists in mocked map and also unable to verify whether register() is called on that object. Below are the errors thrown in the test

Assert.assertNotNull(locationDataMonitor); //Throws assertionError
Mockito.verify(locationDataMonitor, Mockito.times(1)).register()); //org.mockito.exceptions.misusing.NullInsteadOfMockException: 

Implementation

public class DataAcquisitionService {

  private Map<String, IDataMonitor> dataMonitors;

  public DataAcquisitionService (...) {
     this.dataMonitors = new ConcurrentHashMap<>();
  } 

  public void doSomething(String id) {      
    IDataMonitor dataMonitor = null;
    if (this.dataMonitors.containsKey(locationId)) {
        dataMonitor = this.dataMonitors.get(locationId);
        dataMonitor.register();
    }
  }
}

Test

@RunWith(PowerMockRunner.class)
public class DataAcquisitionServiceTest {
    @Mock
    private Map<String, IDataMonitor> dataMonitors;

    @Before
    public void setUp() {   
       MockitoAnnotations.initMocks(this);  
       locationDataMonitors = Mockito.mock(ConcurrentHashMap.class);    
       this.target = new DataAcquisitionService(..);
   }

   @Test
   public void test_doSomething() { 
       String id = "id1";   
       this.target.doSomething(id);
       IDataMonitor locationDataMonitor = this.dataMonitors.get(id)
       Assert.assertNotNull(locationDataMonitor);
       Mockito.verify(locationDataMonitor, Mockito.times(1)).register();  
   }
}

Upvotes: 0

Views: 1760

Answers (2)

basst314
basst314

Reputation: 184

Looks like you want to hold IDataMonitor objects in a collection within your service and try to call a method (register()) on these objects under certain circumstances.

The approach here would be to mock an IDataMonitor object, put it into the collection (probably by calling an existing public method on your DataAcquisitionService) and then run the test.

The inner collection in use should be irrelevant for the test.

I can only assume you have a method like this, to get your IDataMonitors into the service:

  // within DataAcquisitionService
  ...
  public void addMonitor(IDataMonitor monitor) {
    this.dataMonitors.put(monitor.getId(), monitor);
  }

Then you could do something like this in your test:

    private IDataMonitor monitor;

    @Before
    public void setUp() {   
       ...  
       this.monitor = Mockito.mock(IDataMonitor.class); 
       Mockito.when(monitor.getId()).thenReturn("id1");

       this.target = new DataAcquisitionService(..);
       this.target.addMonitor(monitor); // now monitor is in the map
    }

    @Test
    public void test_doSomething() { 
       String id = "id1"; 

       // as our monitor mock with "id1" is in the map, it is found and the register() method is called
       this.target.doSomething(id);

       // verify method call on the mocked monitor
       Mockito.verify(this.monitor, Mockito.times(1)).register(); 
    }

Hope this helps!

Upvotes: 1

GhostCat
GhostCat

Reputation: 140467

You only mock objects that need to be controlled.

You should never ever mock simple collection instances. You simply pass around collection objects that carry the content required to make your code under test the expected path. In your case, you could simply pass an empty map to that class. And after invoking the method under test you check if that map contains the required content.

And to verify the content of a map you simply query the map for its content. For example by using the assertThat assert together with the hamcrest is matcher.

But just to be precise: you don't want to write tests that need to know about such implementation details. You should test the public contracts of your methods instead of exposing the fact that you are using a map to hold values.

Finally: it seems that you don't understand what mock objects are actually about. They are mocks that appear to be instances of a certain class. But they absolutely do not know anything about the real class. A mocked map doesn't store keys and values. The only thing that you can do is to specify respectively verify what method calls are expected to happen for that mock object.

Upvotes: 2

Related Questions