Home > Back-end >  How to mock a bean with @Qualifier
How to mock a bean with @Qualifier

Time:02-08

I have a class which has a Bean with @Qualifier (See AerospikeClient). While writing test cases, I am unable to mock the bean using @MockBean. I always get null pointer exception for aerospikeClient inside verify(aerospikeClient).put

My class

package com.a.recharge.service.impl;

import com.aerospike.client.*;
import com.aerospike.client.policy.WritePolicy;
import com.a.recharge.entity.aero.PacksInfo;
import com.a.recharge.service.AerospikeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class AerospikeServiceImpl implements AerospikeService {

    @Autowired
    @Qualifier("oldAerospikeClient")
    private AerospikeClient aerospikeClient;

    @Value("${config.aerospike.defaultNS:test}")
    private String nameSpace;

    @Value("${config.aerospike.set.txn:''}")
    private String setname;

    @Value("${config.aerospike.ttl:1800}")
    private int ttl;

    @Value("${config.aerospike.defaultNS}")
    public String defaultNS;

    @Value("${config.aerospike.host}")
    private String aerospikeHost;

    @Value("${config.aerospike.port}")
    private String aerospikePort;

    @Value("${config.aerospike.userName}")
    public String aerospikeUserName;

    @Value("${config.aerospike.password}")
    public String aerospikePassword;

    @Value("${config.aerospike.expiration}")
    public long expiration;

    @Value("${config.aerospike.expiration.no}")
    public long noExpiration;


    @Override
    public void insertRecord(String keyString, PacksInfo packsInfo) {
        // log.info("Saving Records in Aerospike, "   keyString  ", "  packsInfo);
        WritePolicy writePolicy = new WritePolicy();
        writePolicy.expiration = ttl; // seconds
        writePolicy.sendKey = true;

        // columns
        Bin[] bins = new Bin[3];

        bins[0] = new Bin("type", packsInfo.getClass().getName());
        bins[2] = new Bin("@user_key", keyString);
        bins[1] = new Bin("value", packsInfo);

        try {
            Key key = new Key("test", "PacksInfo", keyString);
            aerospikeClient.put(writePolicy, key, bins);
        } catch (AerospikeException e) {
            log.error("Exception raised. Root Cause {}, Message {}", ExceptionUtils.getRootCause(e),
                      ExceptionUtils.getMessage(e));
        }
    }

    

}

My test class

package com.a.recharge.service;

import com.aerospike.client.AerospikeClient;
import com.aerospike.client.Bin;
import com.aerospike.client.Host;
import com.aerospike.client.Key;
import com.aerospike.client.policy.ClientPolicy;
import com.aerospike.client.policy.WritePolicy;
import com.a.recharge.config.AerospikeConfiguration;
import com.a.recharge.service.impl.AerospikeServiceImpl;
import com.a.recharge.util.CollectionUtil;
import com.a.recharge.util.TestUtil;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.junit4.SpringRunner;
import org.testng.annotations.BeforeMethod;

import java.util.List;
import java.util.stream.Collectors;

import static org.junit.Assert.assertEquals;

@RunWith(MockitoJUnitRunner.class)
// @SpringBootTest(classes = AerospikeServiceImpl.class)

public class AerospikeServiceImplTest {

    @Mock(name ="oldAerospikeClient")
    private AerospikeClient aerospikeClient;// = TestUtil.getAerospikeClient();

    @Captor
    private ArgumentCaptor<WritePolicy> captor1;

    @Captor
    private ArgumentCaptor<Key> captor2;

    @Captor
    private ArgumentCaptor<Bin[]> captor3;

    @InjectMocks
//    @Autowired
    AerospikeServiceImpl aerospikeService;

//    @BeforeMethod(alwaysRun = true)
//    public void setUp() {
//        MockitoAnnotations.initMocks(this);
//    }


    @Test
    public void checkInsert(){
        Mockito.doNothing().when(aerospikeClient).put(Mockito.any(), Mockito.any(), Mockito.any());
        Mockito.verify(aerospikeClient).put(captor1.capture(), captor2.capture(), captor3.capture());
        aerospikeService.insertRecord(TestUtil.keyString, TestUtil.getPacksInfo());

        assertEquals(1800, captor1.getValue().expiration);
        assertEquals(true, captor1.getValue().expiration);
    }
}

i changed to InjectMocks and used Mockito.doNothing(), But still the same error. Is it because the put method of aerospikeClient is final?

Another question is that for testing the AerospikeServiceImpl class should I autowire it directly as above or create a bean like this?

@TestConfiguration
    static class AerospikeServiceImplTestContextConfiguration {

        @Bean
        public AerospikeServiceImpl AerospikeServiceImpl() {
            return new AerospikeServiceImpl();
        }
    }

I am relatively new to Spring Boot, so any best practices' advice will be highly appreciated.

My final test class. It works. No need of @TestConfiguration.

@RunWith(MockitoJUnitRunner.class)
public class AerospikeServiceImplTest {

    @Mock //(name ="oldAerospikeClient")
    private AerospikeClient aerospikeClient;

    @Captor
    private ArgumentCaptor<Key> captor2;

    @InjectMocks
//    @Autowired
    private AerospikeServiceImpl aerospikeService;

    @Test
    public void checkInsert(){
        aerospikeService.insertRecord(TestUtil.keyString, TestUtil.getPacksInfo());

        Mockito.verify(aerospikeClient).put(captor1.capture(), captor2.capture(), captor3.capture());
        assertEquals("test", captor2.getValue().namespace);
        assertEquals("PacksInfo", captor2.getValue().setName);
        assertEquals("MOBILE_JIOPREPAID_GJ", captor2.getValue().userKey.toString());

    }

CodePudding user response:

aerospikeClient.put() is a final method. Mockito 2 now supports mocking final methods but this feature has to be explicitly activated ; it can be done via the mockito extension mechanism by creating the file src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker containing a single line:

mock-maker-inline Please refer to this link: https://github.com/mockito/mockito/wiki/What's-new-in-Mockito-2#mock-the-unmockable-opt-in-mocking-of-final-classesmethods

  •  Tags:  
  • Related