ATEF CHAREF
ATEF CHAREF

Reputation: 397

how to mock super class that has injected dependencies

I have a project that uses Spring MVC. I'm trying to write unit tests for the service module, which is in the architecture of the project. All service classes extend from super class named "BaseService". BaseService is like this:

public abstract class BaseService {

private static final Logger  logger = LoggerFactory.getLogger(BaseService.class);

@Autowired(required = true)
private HttpServletRequest   request;

@Autowired
private ReloadableResourceBundleMessageSource messageSource;

/*
 * Injecting Mapper
 */
@Resource
private Mapper mapper;
...

public <T extends BaseBVO, S extends BaseVO> T voToBvo (S vo, Class<?     extends BaseBVO> bvoClass) {

    if (vo != null)
    {
        return (T) mapper.map(vo , bvoClass);
    }
    else
    {
        return null;
    }
}

now I have a method in the service module that use the method:

"voToBvo (S vo, Class<?     extends BaseBVO> bvoClass")

like this:

public List<AdcOptionBVO> findOptionByAyantDroitIdAndProduitId (Long idAyantDroit, String idProduit) {

    ....

        list = listVoToBvo(adcList , AdcOptionBVO.class);
        logger.info("Returning List of ayrp count= {}" , list.size());

    return list;
}

My test is like this:

@RunWith(MockitoJUnitRunner.class)
public class AdcServiceImplTest{

@Mock
private Mapper mapper;

    @Test
public void shouldSuccessFindOptionByAyantDroitIdandProduitId() {
    //Given
    List<AdcVO> adcVOList = new ArrayList<>();
    adcVOList.add(new AdcVO());

    List<AdcOptionBVO> listAdcOptionBVO = new ArrayList<>();
    listAdcOptionBVO.add(new AdcOptionBVO());

    List<BaseBVO> baseBVOs = new ArrayList<>();

    //When
    when(repository
            .finAdcByOptionOrderByRUAndPrio(anyLong(), anyString())).thenReturn(adcVOList);

    when(baseService.listVoToBvo(adcVOList,  AdcOptionBVO.class)).thenReturn(baseBVOs);


    //Then
    assertEquals(adcService
            .findOptionByAyantDroitIdAndProduitId(anyLong(), anyString()).size(), adcVOList.size());

}
}

I get a java.lang.NullPointerException in BaseService.java when calling the mapper.

 @Resource
 private Mapper mapper;

the mapper is null!

I want to mock the method:

listVoToBvo(adcList , AdcOptionBVO.class);

Please help.

Upvotes: 5

Views: 12969

Answers (5)

Anzz
Anzz

Reputation: 19

After injecting your mocks, try to initialize them using @Before.

eg:

@Mock
private HttpServletRequest request;

@Mock
private Mapper mapper;

@InjectMocks
private BaseService baseService;

@Before
public void initMocks(){    
    MockitoAnnotations.initMocks(this);
}

Worked for me.

Upvotes: 1

Pramendra Raghuwanshi
Pramendra Raghuwanshi

Reputation: 415

I will suggest you, first to change in your BaseService file. Change the way of writing @Autowired as follows

private HttpServletRequest request;  
@Autowired(required = true)
public void setSpellChecker( HttpServletRequest request ){
   this.request = request;
} 

@Autowired
private ReloadableResourceBundleMessageSource messageSource;
public void setSpellChecker( ReloadableResourceBundleMessageSource messageSource ){
   this.messageSource = messageSource;
}

And when you will do that, now you can easily achieve test-cases with dependency.

Upvotes: 3

ATEF CHAREF
ATEF CHAREF

Reputation: 397

The solution is to add constructor for the abstarct class with injected parameter, in my case ( mapper) , then in the service call super(mapper); in the constructor of the service so in the tests I mock Mapper mapper, then I instanciate my class in @Before, so the mapper pass to the abstarct class as a Mock and not null.. It works fine for me. I hope its claire

Upvotes: 3

DwB
DwB

Reputation: 38338

As your test is currently setup, none of the injected items in the base class are injected. There are two techniques you can use to fix this:

  1. Run using the Spring test runner (named SpringJUnit4ClassRunner.class). This is fine for integration testing, which is out of scope.
  2. Perform the injection by hand. Use reflection and set the mapper in the BaseService class to a mock object.

Use technique 2.

Create a utility that you use for testing that sets a data member using reflection. Perform a google search for "set parent class variable reflection java" or something close to that and you will find example reflection code.d The utility should wrap the reflection code and take parameters for the data member name, the class object, the reference to the object in which to set the value, and anything else needed to perform the reflection.

Another option is to find a reflection utility (google, apache, and spring) should all have one.

Here is a (non-functioning) example:

public static void setFieldObject(
    final String fieldName,
    final Object fieldValue,
    final Object targetObject,
    final Class targetClass)
throws
    NoSuchFieldException,
    IllegalAccessException
{
    final Field targetField;

    targetField = targetClass.getDeclaredField(fieldName);
    targetField.setAccessible(true);

    targetField.set(
        targetObject,
        fieldValue);
}

Upvotes: 2

Mouad EL Fakir
Mouad EL Fakir

Reputation: 3759

Actually your code is somehow not so clear, however using Mockito to mock autowired fields is not so complicated, you have just to follow the right steps.

Let's suppose that we want to create a unit test for BaseService :

public class BaseServiceTest {

    private static final BaseVO TEST_BaseVO = //intialize here...;
    private static final BaseBVO TEST_BaseBVO = //intialize here...;  

    private static final BaseBVO TEST_BaseBVO_RESULT = //intialize here...;

    @InjectMocks
    private BaseService baseService;

    @Mock
    private HttpServletRequest request;

    @Mock
    private ReloadableResourceBundleMessageSource messageSource;

    @Mock
    private Mapper mapper;

    @BeforeMethod
    public void initMocks(){
        //This line of code is so important!! to initialize annotated fields. without it these fields would be null.
        //Or just annotate this class with @RunWith(MockitoJUnitRunner.class)

        MockitoAnnotations.initMocks(this);

        //Here all fields annotated with @Mock or @Spy should be initialize and injected into BaseService.
    }

    @Test
    public void voToBvo_should_return_null_when_vo_is_null(){

        //Call testing method
        BaseBVO result = baseService.voToBvo(null, TEST_BaseBVO.class);

        //Assertions
        Assert.assertNull(result); 
    }

    @Test
    public void voToBvo_should_not_return_null_when_vo_is_not_null(){

        //Mock calls
        Mockito.doReturn(TEST_BaseBVO_RESULT).when(mapper).map(TEST_BaseVO, TEST_BaseBVO.class)

        //Call testing method
        BaseBVO result = baseService.voToBvo(TEST_BaseVO, TEST_BaseBVO.class);

        //Assertions
        Assert.assertNotNull(result); 
        Assert.assertEquals(TEST_BaseBVO_RESULT, result);
    }
}

It may sound to you that this unit test is silly ( actually it is silly :p ) but it describes the basic steps to create a successful UT using Mockito and also how to prevent NPE.

Upvotes: 0

Related Questions