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 functionCalling
IProducerProcessor aigcFlowCostCollectRecordProducerMock = Mock(IProducerProcessor)
def setup() {
functionCalling = new FunctionCalling()
functionCalling.aigcFlowCostCollectRecordProducer = aigcFlowCostCollectRecordProducerMock
}
/**
*对SendCalculateFlowCostMQMessage.create静态方法进行mock
*
*/
@Unroll
def "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需求。