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需求。