Search code examples
javaandroidunit-testingmockitonon-nullable

Mocking a class assigns nulls to @NonNull fields(Android)


I'm using Android SDK and junit4 + Mockito for unit testing. Say I have a class like this in my app:

public class Container{

  @NonNull private Set<String> values = new HashSet<>();

  public void addValue(String value) {
    values.add(value);
  }

  @NonNull
  public Set<String> getValues() {
    return values;
  }
}

And I also have a unit test with Mockito that looks like this:

public class ContainerTest {

  private Container container;

  @Before
  public void before() {
    container = mock(Container.class);
  }

  @Test
  public void shouldAddValue() {
    container.add("test_value");
    assertTrue(container.getValues.contains("test_value"));
  }

}

This test actually fails on line "container.add("test_value");" because mock(Container.class) creates a class in which values field is actually set to null, so values.add(value) in addValue() method throws an NPE. I could add a null check in addValue() to fix this, but that seems absurd, since values are already declared non null.

Is there any way to make Mockito respect @NonNull annotations and initialize the field properly?


Solution

  • I think you aren't using Mockito in the right way, since you have to define the Mockito behaviors.

    For instance, you should have something like this:

    @Test
    public void shouldAddValue() {
        Set<String> mySet = new HashSet<String>();
        mySet.put("test_value");
    
        // Mock container getValues() method to return mySet
        when(container.getValues()).thenReturn(mySet); // do import static for Mockito.when
    
        assertTrue(container.getValues().contains("test_value"));
    }
    

    Mockito works pretty well when you mock responses, but what you want is to let Mockito to initialize classes for you which clearly it isn't Mockito goals.

    Therefore, if you want to test your Container object, then you don't have to mock Container itself and you can have something like this:

    public class ContainerTest {
    
        private Container container;
    
        @Before
        public void before() {
            container = new Container(); // Initialize container
        }
    
        @Test
        public void shouldAddValue() {
            container.addValue("test_value");
            assertTrue(container.getValues().contains("test_value"));
        }
    
    }