java - 如何模拟具有反应核心单声道代码的类
问题描述
我有以下休息控制器,我正在尝试使用 Mockito 进行测试。
@RestController
public class BackupQueueController {
@Autowired
JmsTemplate jmsTemplate;
@Autowired
SettingsService settingsService;
@Resource
Environment env;
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(BackupQueueController.class);
@GetMapping("/eib3/startBackupQueueDraining")
public String startBackupQueueDraining(){
String response= IcisApiServiceConstant.EMPTY;
final String backupQueueEndpoint = env.getProperty("record_order_backup_queue");
final String origQueueEndPoint=env.getProperty("record_order_queue");
final String logPrefix=System.currentTimeMillis()+"-"+" :: BackupQueueController :: startBackupQueueDrain :: " ;
final int totalMessages=getNumberOfMessageInQueue(backupQueueEndpoint);
LOGGER.info(logPrefix+" started .");
Mono.fromRunnable( () -> {
//this block of code will be executed in separate thread!
try {
//get total count of messages in backup queue.
for(int i=0;i<totalMessages;i++) {
//get message from backup queue.
if(StringUtils.equalsIgnoreCase(settingsService.getSettingsProperties().getAllowDrain(),IcisApiServiceConstant.TRUE))
{
String msgPayload = (String) jmsTemplate.receiveAndConvert(backupQueueEndpoint);
AddSalesOrderDocument addSalesOrderDocument = AddSalesOrderDocument.Factory.parse(msgPayload);
String orderId = addSalesOrderDocument.getAddSalesOrder().getDataArea().getSalesOrder().getHeader().getDocumentIds().getTransactionId().getId();
LOGGER.info(logPrefix + " order Id ::" + orderId + " :: request payload :: \n" + msgPayload);
// put message onto original queue.
jmsTemplate.convertAndSend(origQueueEndPoint,msgPayload);
//now thread.sleep
Thread.sleep(Long.parseLong(settingsService.getSettingsProperties().getDrainRate()));
}else{
LOGGER.info(logPrefix+" Allow Drain :: "+settingsService.getSettingsProperties().getAllowDrain() +" :: exiting from backup queue draining process ");
break;
}
}
}catch (Exception e){
LOGGER.error(logPrefix,e.getMessage());
}
}).subscribeOn(Schedulers.newElastic("backupQueue"))
.subscribe();
response+=" draining process has started to drain "+totalMessages+" orders.";
return response;
}
public int getNumberOfMessageInQueue(String queueName) {
Mono<Integer> messageCount= Mono.fromCallable( () -> {
return jmsTemplate.browse(queueName, (session, queueBrowser) ->
Collections.list(queueBrowser.getEnumeration()).size()
);
}).timeout(Duration.ofSeconds(5L)).subscribeOn(Schedulers.elastic());
return messageCount.block(); // Getting null pointer exception while testing //with junit
}
}
控制器使用以下类
@Service
@RefreshScope
public class SettingsService {
@Autowired
private SettingsProperties properties;
public SettingsProperties getSettingsProperties() {
return this.properties;
}
}
@ConfigurationProperties(prefix = "app")
@Component
public class SettingsProperties {
private String name;
private String recordOrderQueue;
private String drainRate;
private String allowDrain;
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(SettingsProperties.class);
public String getDrainRate() {
return drainRate;
}
public void setDrainRate(String drainRate) {
LOGGER.info(getLogPrefix()+ ":: setDrainRate :: drain rate"+drainRate);
this.drainRate = drainRate;
}
public String getRecordOrderQueue() {
return recordOrderQueue;
}
public void setRecordOrderQueue(String recordOrderQueue) {
LOGGER.info(getLogPrefix()+ ":: setRecordOrderQueue :: recordOrderQueue"+recordOrderQueue);
this.recordOrderQueue = recordOrderQueue;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAllowDrain() {
return allowDrain;
}
public void setAllowDrain(String allowDrain) {
LOGGER.info(getLogPrefix()+ ":: setAllowDrain :: allowDrain"+allowDrain);
this.allowDrain = allowDrain;
}
public static String getLogPrefix(){
return System.currentTimeMillis()+" SettingsProperties :: ";
}
@PostConstruct
public void postConstruct() {
LOGGER.info("postConstruct: {}@{} {}",
this.getClass().getSimpleName(), System.identityHashCode(this), this);
}
}
我写了以下测试用例
@RunWith(SpringRunner.class)
public class BackupQueueControllerTest {
@InjectMocks
BackupQueueController backupQueueController= new BackupQueueController();
@Mock
JmsTemplate jmsTemplate;
@Mock
SettingsService settingsService;
@Mock
Environment env;
private static final String ORDER ="<document> <orderId> 1231</orderId></document>";
@Test
public void startBackupQueueDrainingTest(){
when(env.getProperty("record_order_backup_queue")).thenReturn("fake_backup_queue");
when(backupQueueController.getNumberOfMessageInQueue("fake_backup_queue")).thenReturn(10);
when(jmsTemplate.receiveAndConvert("fake_backup_queue")).thenReturn(ORDER);
when(settingsService.getSettingsProperties().getAllowDrain()).thenReturn("yes");
when(settingsService.getSettingsProperties().getDrainRate()).thenReturn("2000");
doNothing().when(jmsTemplate).convertAndSend(anyString(),anyString());
assertEquals(backupQueueController.startBackupQueueDraining(),"draining process has started to drain "+10+" orders.");
}
}
上面的测试用例在 messageCount.block() 处抛出空指针异常
现在我有以下问题。
如何模拟 getNumberOfMessageInQueue() 方法?
如何模拟 settingsService.getSettingsProperties().getAllowDrain() ?
解决方案
回答你的第一个问题:你不能模拟你实际测试的类的方法。如果你想模拟它,你应该将它提取到一个自己的类中,并在你的测试期间注入这个类的模拟,就像你已经做的那样SettingsService
。
当前测试中的 NullPointerException 来自 not mocking jmsTemplate.browse(queueName, (session, queueBrowser)
。您应该模拟您所依赖的 bean 的每个调用,否则,它们的方法调用将返回null
,因为您没有指定它们的行为。
回答你的第二个问题:你首先必须模拟结果settingService.getSettingsProperties()
并返回一个SettingsProperties
模拟然后模拟这个对象settingsProperties.getAllowDrain()
,例如:
@Mock
private SettingsService settingsService;
@Mock
private SettingsProperties settingsProperties;
@Test
public void test() {
when(settingsService.getSettingsProperties()).thenReturn(settingsProperties);
when(settingsProperties.getAllowDrain()).thenReturn("FOO");
}
如果你模拟你的类的依赖,永远不要忘记模拟你在业务逻辑中访问的所有方法,否则 Mockito 会返回null
推荐阅读
- c# - SQL 如何替换一个值
- python - 使用 python 和请求,如何进行正确的 POST 调用以及在哪里找到标头?
- android - FirebaseAppDistribution:appDistributionUpload gradle 命令中缺少应用程序 ID
- mysql - 跨列和行计算值的实例
- javascript - 使用 redux 钩子时使用 redux 操作的最佳选择是什么?
- sql - Impala 2.11:AnalysisException:选择列表中不支持子查询
- swift - 在 SwiftUI 中获取当前的经纬度
- c++ - 是否可以禁止一个类被动态转换为?
- git - Git - 将发布分支合并到主分支
- tensorflow - 任何想法如何解决激活函数的问题?