在原先的文章 真香系列之2-自动录制回放的Hoverfly-java-Junit5 中,笔者提到了关于录制文件中数据修改的问题。
在实际的项目中,当服务间进行内部服务调用时,出于鉴权的需要,会在请求体中带上timeStamp,token等信息。这些信息经过录制之后会存放在指定的JSON文件之中。为了能够在用例执行时,可以让用例能够正确执行,需要手工将JSON文件中的匹配模式修改为glob,并将中的timeStamp,token的具体值修改为通配符*。如以下的案例,
"body": [ {
"matcher": "glob",
"value": "timeStamp=*,token=*" }
]
在目前的方案中,通过在录制并完成写文件之后重新进行调整的方式实现了修改。也就是
1)继承HoverflyExtension并复写afterAll方法,
2)首先根据现有方法来生成JSON文件,
3)然后根据capturePath来获取已生成的文件,并编写modify方法来修改并保存这个文件。
实际上,Hoverfly提供了SimulationPreprocessor 这一机制来让用户动态修改模拟数据。
The
SimulationPreprocessor
interface lets you apply custom transformation to theSimulation
object before importing to Hoverfly. This can be useful if you want to batch add/remove matchers, or update matcher types, like weakening matching criteria of captured data. https://hoverfly-java.readthedocs.io/en/latest/pages/corefunctionality/configuration.html#simulation-preprocessor
而在Hoverfly-java中,也为此提供了一个SimulationPreprocessor类,这个功能接口类只含有一个方法accept(Simulation simulation) 。
ackage io.specto.hoverfly.junit.core;
import io.specto.hoverfly.junit.core.model.Simulation;
import java.util.function.Consumer;
/**
* A SimulationPreprocessor processes{@link Simulation} instances prior to
* handing them over to Hoverfly client. As {@link Simulation} instances are
* mutable, you may directly modify the simulation be adding additional
* request-response-pairs or weakening for example request matching of
* previously captured session.
*/
@FunctionalInterface
public interface SimulationPreprocessor extends Consumer<Simulation> {
/**
* {@inheritDoc}
* <p>
* Allows to modify the given mutable {@link Simulation} instance,
* by, for example, adapting request matching.
* </p>
*/
@Override
void accept(Simulation simulation);
}
与之前的案例类似,选取Hoverfly官网提供的测试案例, 在模拟文件中有如下的请求需要修改为通配。
"body": [
{
"matcher": "exact",
"value": "{\"flightId\": \"1\"}"
}
]
可以通过定义一个类来实现这个接口,然后完成对数据的运行时修改。
package io.specto.hoverfly.junit5;
import io.specto.hoverfly.junit.core.Hoverfly;
import io.specto.hoverfly.junit.core.SimulationPreprocessor;
import io.specto.hoverfly.junit.core.model.RequestFieldMatcher;
import io.specto.hoverfly.junit.core.model.RequestResponsePair;
import io.specto.hoverfly.junit.core.model.Simulation;
import io.specto.hoverfly.junit5.api.HoverflyConfig;
import io.specto.hoverfly.junit5.api.HoverflySimulate;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
@HoverflySimulate(
config = @HoverflyConfig(
simulationPreprocessor = PreProcessTest.CustomSimulationPreprocessor.class
), source = @HoverflySimulate.Source(value = "hoverfly/test-service.json",
type = HoverflySimulate.SourceType.CLASSPATH)
)
@ExtendWith(HoverflyExtension.class)
public class PreProcessTest {
static class CustomSimulationPreprocessor implements SimulationPreprocessor {
@Override
public void accept(Simulation simulation) {
//遍历请求响应数据,找出请求中的请求体中的body,如果body中包含了flightId,
// 则将matcher修改为glob, flightId的值修改为*。
Set<RequestResponsePair> pairs= simulation.getHoverflyData().getPairs();
pairs.forEach(requestResponsePair -> requestResponsePair.getRequest().getBody()
.stream().forEach(body->{
if( ((String) body.getValue()).contains("flightId")){
body.setMatcher(RequestFieldMatcher.MatcherType.GLOB);
body.setValue("*");
}}));
}
}
}
然后在这个类中写一个单元测试用例来测试一下。
@Test
void shouldUseCustomPorts(Hoverfly hoverfly) {
Set<RequestResponsePair> pairs= hoverfly.getSimulation().getHoverflyData().getPairs();
pairs.forEach(requestResponsePair -> requestResponsePair.getRequest().getBody()
.stream().forEach(body->{
if( ((String) body.getValue()).contains("flightId")){
assertThat(body.getMatcher()).isEqualTo(RequestFieldMatcher.MatcherType.GLOB);
}
}));
}
可以看到,虽然在模拟文件中的matcher为exact,但是经过在CustomSimulationPreprocessor类中的修改之后,从测试用例中获取到的hoverfly实例中所包含的该数据已经被修改成了glob。也就是动态修改成功了。
当然,相比于在录制时就地修改的一劳永逸,用这种方式来修改请求其实也比较繁琐,需要在各个引用此模拟文件的类中使用CustomSimulationPreprocessor。笔者看来,这个方式更合适的应用场景应该是扮演Hoverfly Middleware的作用,也就是通过这个接口来动态修改响应结果,类似DataBaseRider中的动态数据替换,如NOW占位符则自动返回系统当前时间。感兴趣的读者可以参考示例自行实现。