如何使用@Qualifier 模拟一个bean
How to mock a bean with @Qualifier
我有一个 class,它有一个带有 @Qualifier 的 Bean(参见 AerospikeClient)。在编写测试用例时,我无法使用 @MockBean 模拟 bean。我总是在 verify(aerospikeClient).put
中得到 aerospikeClient 的空指针异常
我的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));
}
}
}
我的测试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);
}
}
我改成InjectMocks,用了Mockito.doNothing(),还是一样的错误。
是不是因为aerospikeClient的put方法是final的?
另一个问题是为了测试 AerospikeServiceImpl class 我应该像上面那样直接自动装配它还是像这样创建一个 bean?
@TestConfiguration
static class AerospikeServiceImplTestContextConfiguration {
@Bean
public AerospikeServiceImpl AerospikeServiceImpl() {
return new AerospikeServiceImpl();
}
}
我对 Spring Boot 比较陌生,因此非常感谢任何最佳实践建议。
我的期末考试class。有用。不需要@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());
}
aerospikeClient.put() 是最后一个方法。 Mockito 2 现在支持模拟 final 方法,但必须显式激活此功能;它可以通过 mockito 扩展机制通过创建包含一行的文件 src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker 来完成:
mock-maker-inline
请参考这个link:https://github.com/mockito/mockito/wiki/What's-new-in-Mockito-2#mock-the-unmockable-opt-in-mocking-of-final-classesmethods
我有一个 class,它有一个带有 @Qualifier 的 Bean(参见 AerospikeClient)。在编写测试用例时,我无法使用 @MockBean 模拟 bean。我总是在 verify(aerospikeClient).put
中得到 aerospikeClient 的空指针异常我的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));
}
}
}
我的测试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);
}
}
我改成InjectMocks,用了Mockito.doNothing(),还是一样的错误。 是不是因为aerospikeClient的put方法是final的?
另一个问题是为了测试 AerospikeServiceImpl class 我应该像上面那样直接自动装配它还是像这样创建一个 bean?
@TestConfiguration
static class AerospikeServiceImplTestContextConfiguration {
@Bean
public AerospikeServiceImpl AerospikeServiceImpl() {
return new AerospikeServiceImpl();
}
}
我对 Spring Boot 比较陌生,因此非常感谢任何最佳实践建议。
我的期末考试class。有用。不需要@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());
}
aerospikeClient.put() 是最后一个方法。 Mockito 2 现在支持模拟 final 方法,但必须显式激活此功能;它可以通过 mockito 扩展机制通过创建包含一行的文件 src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker 来完成:
mock-maker-inline 请参考这个link:https://github.com/mockito/mockito/wiki/What's-new-in-Mockito-2#mock-the-unmockable-opt-in-mocking-of-final-classesmethods