简体   繁体   English

集成测试 flink 作业

[英]Integration testing flink job

I've written a small flink application.我写了一个小的 flink 应用程序。 I has some input, and enriches it with data from an external source.我有一些输入,并用外部来源的数据丰富了它。 It's an RichAsyncFunction and within the open method I construct a http client to be used for the enrichment.它是一个RichAsyncFunction ,在open方法中,我构建了一个 http 客户端用于丰富。

Now I want to write an integration test for my job.现在我想为我的工作编写一个集成测试。 But since the http client is created within the open method I have no means to provide it, and mock it in my integration test.但是由于 http 客户端是在 open 方法中创建的,我无法提供它,也无法在我的集成测试中模拟它。 I've tried to refactor it providing it within the constructor, but I'm always getting serialisation errors.我试图在构造函数中提供它来重构它,但我总是遇到序列化错误。

This is the example I'm working from: https://ci.apache.org/projects/flink/flink-docs-release-1.10/dev/stream/operators/asyncio.html这是我正在使用的示例: https://ci.apache.org/projects/flink/flink-docs-release-1.10/dev/stream/operators/asyncio.html

Thanks in advance:)提前致谢:)

This question was posted over a year ago but I'll post the answer in-case anyone stumbles upon this in the future.这个问题是在一年前发布的,但我会发布答案,以防将来有人偶然发现这个问题。

The serialization exception you are seeing is likely this您看到的序列化异常可能是这样的

Exception encountered when invoking run on a nested suite. *** ABORTED *** (610 milliseconds)
  java.lang.NullPointerException:
  at java.util.Objects.requireNonNull(Objects.java:203)
  at org.apache.flink.streaming.runtime.streamrecord.StreamElementSerializer.<init>(StreamElementSerializer.java:64)
  at org.apache.flink.streaming.api.operators.async.AsyncWaitOperator.setup(AsyncWaitOperator.java:136)
  at org.apache.flink.streaming.api.operators.SimpleOperatorFactory.createStreamOperator(SimpleOperatorFactory.java:77)
  at org.apache.flink.streaming.api.operators.StreamOperatorFactoryUtil.createOperator(StreamOperatorFactoryUtil.java:70)
  at org.apache.flink.streaming.util.AbstractStreamOperatorTestHarness.setup(AbstractStreamOperatorTestHarness.java:366)
  at org.apache.flink.streaming.util.OneInputStreamOperatorTestHarness.setup(OneInputStreamOperatorTestHarness.java:165)
...

The reason is that your test operator needs to know how to deserialize the DataStream input type.原因是您的测试操作员需要知道如何反序列化 DataStream 输入类型。 The only way to provide this is by supplying it directly while initializing the testHarness and then passing it to the setup() method call.提供此功能的唯一方法是在初始化testHarness时直接提供它,然后将其传递给setup()方法调用。

So to test the example from the Flink docs you linked you can do something like this (my implementation is in Scala but you can adapt it to Java as well)因此,要测试您链接的Flink 文档中的示例,您可以执行以下操作(我的实现在 Scala 但您也可以将其调整为 Java)

import org.apache.flink.api.common.ExecutionConfig
import org.apache.flink.api.java.typeutils.TypeExtractor
import org.apache.flink.configuration.Configuration
import org.apache.flink.streaming.api.datastream.AsyncDataStream.OutputMode
import org.apache.flink.streaming.api.operators.async.AsyncWaitOperator
import org.apache.flink.streaming.runtime.tasks.{StreamTaskActionExecutor, TestProcessingTimeService}
import org.apache.flink.streaming.runtime.tasks.mailbox.{MailboxExecutorImpl, TaskMailboxImpl}
import org.apache.flink.streaming.util.OneInputStreamOperatorTestHarness
import org.scalatest.{BeforeAndAfter, FunSuite, Matchers}


/**
 This test case is written using Flink 1.11+.
 Older versions likely have a simpler constructor definition for [[AsyncWaitOperator]] so you might have to remove the last two arguments (processingTimeService and mailboxExecutor)
*/
class AsyncDatabaseRequestSuite extends FunSuite with BeforeAndAfter with Matchers {

  var testHarness: OneInputStreamOperatorTestHarness[String, (String, String)] = _
  val TIMEOUT = 1000
  val CAPACITY = 1000
  val MAILBOX_PRIORITY = 0

  def createTestHarness: Unit = {
    val operator = new AsyncWaitOperator[String, (String, String)](
      new AsyncDatabaseRequest {

        override def open(configuration: Configuration): Unit = {
          client = new MockDatabaseClient(host, post, credentials);  // put your mock DatabaseClient object here
        }
      },
      TIMEOUT,
      CAPACITY,
      OutputMode.UNORDERED,
      new TestProcessingTimeService,
      new MailboxExecutorImpl(
        new TaskMailboxImpl,
        MAILBOX_PRIORITY,
        StreamTaskActionExecutor.IMMEDIATE
      )
    )

    // supply the TypeSerializer for the "input" type of the operator
    testHarness = new OneInputStreamOperatorTestHarness[String, (String, String)](
      operator,
      TypeExtractor.getForClass(classOf[String]).createSerializer(new ExecutionConfig)
    )

    // supply the TypeSerializer again to the setup() call
    testHarness.setup(
      TypeExtractor.getForClass(classOf[String]).createSerializer(new ExecutionConfig)
    )
    testHarness.open()
  }

  before {
    createTestHarness
  }

  after {
    testHarness.close()
  }

  test("Your test case goes here") {
    // fill in your test case here
  }

}

Here is the solution in Java这是Java中的解决方案

class TestingClass {

@InjectMocks
ClassUnderTest cut;

private static OneInputStreamOperatorTestHarness<IN, OUT> testHarness; // replace IN, OUT with your asyncFunction's
private static long TIMEOUT = 1000;
private static int CAPACITY = 1000;
private static int MAILBOX_PRIORITY = 0;
private long UNUSED_TIME = 0L;

Driver driverRef;

public void createTestHarness() throws Exception {

    cut = new ClassUnderTest() {

        @Override
        public void open(Configuration parameters) throws Exception {
            driver = mock(Driver.class); // mock your driver (external data source here).
            driverRef = driver; // create external ref to driver to refer to in test
        }
    };

    MailboxExecutorImpl mailboxExecutorImpl = new MailboxExecutorImpl(
            new TaskMailboxImpl(), MAILBOX_PRIORITY, StreamTaskActionExecutor.IMMEDIATE
    );

    AsyncWaitOperator operator  = new AsyncWaitOperator<>(
            gatewayEnrichment,
            TIMEOUT,
            CAPACITY,
            ORDERED,
            new TestProcessingTimeService(),
            mailboxExecutorImpl
    );

    testHarness = new OneInputStreamOperatorTestHarness<IN, OUT>(
            operator,
            TypeExtractor.getForClass(IN.class).createSerializer(new ExecutionConfig())
    );

    testHarness.setup(TypeExtractor.getForClass(OUT.class).createSerializer(new ExecutionConfig()));
    testHarness.open();
    }

    @BeforeEach()
    void setUp() throws Exception {
        createTestHarness();
        MockitoAnnotations.openMocks(this);
    }

    @AfterEach
    void tearDown() throws Exception {
       testHarness.close();
    }

    @Test
    public void test_yourTestCase() throws Exception { 
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM