Java Apache Beam,使用构造函数变量在 DoFn 的 @Setup 方法中初始化模拟外部客户端

问题描述 投票:0回答:1

Apache Beam 建议使用 Fakes 而不是 Mocks,因为 Mocks 无法通过管道进行序列化。

我正在为旧代码编写单元测试,其中类使用 Apache Beam 调用其他外部类服务(我尚未编写或无法更改)。 * serviceApi 不使用接口,但是如果需要的话可以包装在一个接口中。

public class DataAdapter extends DoFn<KV<String, Product>, Void>{
    public String processCategory;
    public Map<String, String> configs;
    public transient ServiceApi serviceApi; 
    
    public DataAdapter(String processCategory, Map<String, String> configs) {
        this.processCategory = processCategory;
        this.configs = configs;
    }
  
    @Setup
    public void init() {
        serviceApi = new ServiceApi(processCategory, configs);
    }

    @ProcessElement
    public void processData(ProcessContext context, @Element KV<String, Product> input) {
        try {
            String productId = input.getKey();
            serviceApi.getData();
            ....
     }
} 

尝试:

我在下面创建了假的。使用 null 调用构造函数将导致 serviceApi 构造函数中出现 Argument Exception 错误。所以我尝试了工厂创建模式,这可能并不理想。 (尝试寻找其他解决方案)

然而,假的不断被覆盖,因为对于每个

ProcessElement
,它都会调用
Setup
,并重新初始化父类。我们必须在每个 SetupBatch 中进行初始化,因为 serviceApi 在运行 Apache Beam 应用程序时不可序列化。

创造假货:

public class ServiceApiFake extends ServiceApi {

    public ServiceApiFake() {
        super(null, null);
    }

    public static ServiceApiFake create() {
        try {
            return new ServiceApiFake();
        } catch (Exception ignore) {
            return null;
        }
    }

    @Override
    public DataItem getData(String keyId) {
        return null;
    }
}

测试:

TestStream<KV<String, Product>> testStream = TestStream.create(KvCoder.of(StringUtf8Coder.of(), SerializableCoder.of(Product.class)))
            .addElements(KV.of("25", product)).advanceWatermarkToInfinity();

DataAdapter dataAdapter = new DataAdapter("productCategory", configs);
dataAdapter.serviceApi = new ServiceApiFake();
pipeline.apply(testStream).apply(ParDo.of(dataAdapter));
pipeline.run();

测试运行时出错:安装程序初始化时出现 ArgumentExceptionError,因为它 返回在设置中调用常规服务而不是假的。

我考虑通过调用“processData”方法来单独测试方法,而不调用 setup,并在 ProcessContext、ElementInput 和

serviceApi when(serviceApi.getData()).thenReturn(..)
上使用mockito Mock。这个策略确实有效,虽然并不理想,但允许我在某种程度上进行测试。请注意,Api 是瞬态属性,否则应用程序将出错。 serviceApi 不使用接口,但如果需要,可以包装在一个接口中。这是较旧的项目,不使用 Spring Boot 或 GoogleGuice 注入器。

我希望 Apache Beam 团队在使用假货方面有更好、更强大的替代方案,或者以某种方式允许模拟。或者再次允许 DoFnTester(因为目前已弃用)。公司政策不允许再使用已弃用的用途。

资源:我查看了参考stackoverflow文章。他们的 serviceAPI 在 serviceAPI 中没有构造函数变量或参数异常验证器,因此它适合他们的情况。它还使用 VisibleForTesting Annotation,在我们公司代码库中不推荐。

Apache Beam,在 DoFn 的 @Setup Lifecycle 方法中初始化的模拟外部客户端

java mockito google-cloud-dataflow apache-beam
1个回答
0
投票

我过去使用的解决方案是:不要在@SetUp中初始化。使用标准接口依赖注入到构造函数中,并为真实管道传递真实管道,为测试传递虚假管道。

您可以通过提供用于非测试目的的辅助静态方法,进一步将其从转换的用户中抽象出来:

static PTransform<PCollection<? extends InputT>, PCollection<Void>> create(String processCategory, Map<String, String> configs) {
  return ParDo.of(new DataAdapter(new ServiceApi(processCategory, configs)));
}
© www.soinside.com 2019 - 2024. All rights reserved.