SpockMockStatic方法
參考: https://blog.csdn.net/knighttools/article/details/44630975
?
static方法
import com.meituan.mafka.client.producer.IProducerProcessor;
import com.meituan.mdp.langmodel.api.message.AssistantMessage;
import com.sankuai.gaigc.arrange.common.core.promptflow.entity.aigc.flow.cost.collect.AigcFlowCostCollect;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;import java.util.Optional;/*** 計算費用flow調用model產生的資費實體*/
@Slf4j
public class SendCalculateFlowCostMQMessage {private IProducerProcessor aigcFlowCostCollectRecordProducer;public SendCalculateFlowCostMQMessage(IProducerProcessor aigcFlowCostCollectRecordProducer) {this.aigcFlowCostCollectRecordProducer = aigcFlowCostCollectRecordProducer;}//mock 這個靜態方法.public static SendCalculateFlowCostMQMessage create(IProducerProcessor aigcFlowCostCollectRecordProducer) {return new SendCalculateFlowCostMQMessage(aigcFlowCostCollectRecordProducer);}/*** 發送計算費用的MQ消息。*/public void send(String flowId, String appId, String modelName, AssistantMessage assistantMessage) {//... //內部方法....AigcFlowCostCollect aigcFlowCostCollect = AigcFlowCostCollect.create(xxx,xxx,xxx );aigcFlowCostCollect.sendMsg(aigcFlowCostCollectRecordProducer);//...}
?
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dianping.cat.Cat;
import com.meituan.mafka.client.producer.IProducerProcessor;
import com.meituan.mafka.client.producer.ProducerResult;
import com.meituan.mafka.client.producer.ProducerStatus;
import com.sankuai.gaigc.arrange.common.util.RetryUtils;
import com.sankuai.gaigc.arrange.dao.dal.entity.AigcFlowCostCollectRecordDO;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.Objects;/*** flow計費實體*/
@Slf4j
public class AigcFlowCostCollect {public ProducerResult sendMsg(IProducerProcessor producerProcessor) {// 重試3次return RetryUtils.withRetry(() -> doSendMsg(producerProcessor), 3);}private ProducerResult doSendMsg(IProducerProcessor producerProcessor) {try {String jsonString = JSONObject.toJSONString(this);// mock 這兒的方法...ProducerResult producerResult = producerProcessor.sendMessage(jsonString);return producerResult;} catch (Exception e) {throw new RuntimeException("記錄flow費用異常");}}
}
spock-PowerMockito
?
注意事項.
測試類上打 @RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(Sputnik.class) 這兩個注解
然后測試類上打@PrepareForTest(SendCalculateFlowCostMQMessage.class) , 其中SendCalculateFlowCostMQMessage就是你要mock的靜態方法所在的類
import org.assertj.core.util.Lists
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PowerMockIgnore
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
import org.powermock.modules.junit4.PowerMockRunnerDelegate
import org.spockframework.runtime.Sputnik
import spock.lang.Specification
import spock.lang.Unroll@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(Sputnik.class)
@PrepareForTest(SendCalculateFlowCostMQMessage.class)
@PowerMockIgnore("javax.net.ssl.*")
class FunctionCallingSpec extends Specification {FunctionCalling functionCallingIProducerProcessor aigcFlowCostCollectRecordProducerMock = Mock(IProducerProcessor)def setup() {functionCalling = new FunctionCalling()functionCalling.aigcFlowCostCollectRecordProducer = aigcFlowCostCollectRecordProducerMock}/***對SendCalculateFlowCostMQMessage.create靜態方法進行mock* */@Unrolldef "test execute with flow"() {given:PowerMockito.mockStatic(SendCalculateFlowCostMQMessage.class)SendCalculateFlowCostMQMessage create = SendCalculateFlowCostMQMessage.create(aigcFlowCostCollectRecordProducerMock)PowerMockito.when(SendCalculateFlowCostMQMessage.create(Mockito.any(IProducerProcessor.class))).thenReturn(create);//mock方法aigcFlowCostCollectRecordProducerMock.sendMessage(_) >> new ProducerResult(ProducerStatus.SEND_OK)when:Map<String, Object> result = functionCalling.execute()then:result.size() > 0}}
?
可以發現mock成功了.
??
?
原理
PowerMock簡單實現原理
-
當某個測試方法被注解@PrepareForTest標注以后,在運行測試用例時,會創建一個新的org.powermock.core.classloader.MockClassLoader實例,然后加載該測試用例使用到的類(系統類除外)
-
PowerMock會根據你的mock要求,去修改寫在注解@PrepareForTest里的class文件(當前測試類會自動加入注解中),以滿足特殊的mock需求。例如:去除final方法的final標識,在靜態方法的最前面加入自己的虛擬實現等。
-
如果需要mock的是系統類的final方法和靜態方法,PowerMock不會直接修改系統類的class文件,而是修改調用系統類的class文件,以滿足mock需求。