I am trying to Mock an autowired map of instances in spring but I get null pointer exception

In one of my controller

@Autowired
    private Map<String, ABC> abcMap;

now I want mock it in one of the unit test but I always get null pointer exception. This map contains implementations of ABC abstract class.

Can anyone suggest a possible solution?


I'm not sure what Unit test Framework you are using but there are ways of making it inject the mock details. You'll have to give us more information before before we can answer.

Personally I don't much like Autowired private fields, so at the risk of answering a different question can I suggest you consider using an Autowired constructor instead. From Springs POV it won't make a difference, your object will be create and all the appropriate data wired in. (OK, there is a slight change in the order things are done, but generally you won't notice). You will have to write a constructor to copy the constructor parameters to private fields, but:

  1. Those fields could be made final, which could make your class safer
  2. Your Unit tests wont need any 'magic' to initialise the Autowired fields - just pass parameters
  3. If you refactor you class to remove add/remove/modify an Autowired field then you have to remember to change your test code. With an Autowired constructor you test code has to be changed or it won't compile, and your IDE might even help you do it.

Update The Autowired constructor alternative looks something like:

@Controller
class MyClass {
   private final Class1 bean1;
   private final Object value2;

  @Autowired
  MyClass(Class1 bean1, Class2 bean2) {
    this.bean1 = bean1;
    this.value2 = bean2.getValue();
  }
} 

Keys points are:

  1. The class has just one constructor and it requires parameters.
  2. The fields are not annotated @Autowired, because Spring is not assigning values to them; the constructor does that.
  3. The constructor IS annotated as @Autowired to tell Spring to pass the beans as parameters
  4. The first parameter is stored in a final variable - you code can't accidentally over write it, so your code is safer
  5. In my example the second parameter is only used in the constructor, so we don't have to store it as a field in your controller. I often to this if the Bean is an object that passes configuration around.
  6. A No-argument constructor is not required
  7. At test time your code will have to pass parameters to the class.

Your test code will look something like:

class MyClassTest {
  private Class1 bean1;
  private Class2 bean2;
  private MyClass objectUnderTest;

  @Before
  public void setUp() throws Exception {
    bean1 = mock(Class1.class);
    bean2 = mock(Class2.class);
    // Train mocks here

    objectUnderTest = new MyClass(bean1, bean2)
  }

  @Test
  public void myTest() {
     // Do something with objectUnderTest 
  }
}

Key points are:

  1. There are no @MockBean annotations
  2. The Unit test is only using the API that your Controller bean defines; No black magic is required
  3. It's not possible to create a MyClass with out providing the required data. This is enforced by the compiler