From 3207b63c5234899ddca379f9de39d5b2afd70650 Mon Sep 17 00:00:00 2001 From: FuYouJ <1247908487@qq.com> Date: Sun, 20 Aug 2023 19:10:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9example=E5=B7=A5=E7=A8=8B=E7=BB=93?= =?UTF-8?q?=E6=9E=84=E8=BF=9B=E8=A1=8C=E4=BA=86=E9=87=8D=E6=96=B0=E8=A7=84?= =?UTF-8?q?=E5=88=92,=E7=BB=99=E5=87=BA=E4=B8=A4=E4=B8=AA=E7=BC=96?= =?UTF-8?q?=E5=86=99=E6=B5=8B=E8=AF=95=E6=A8=A1=E5=9D=97=E7=9A=84=E6=A1=88?= =?UTF-8?q?=E4=BE=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 对example工程结构进行了重新规划,给出两个编写测试模块的案例。 --- README.md | 5 +- datax-example/datax-example-core/pom.xml | 20 +++ .../datax/example/ExampleContainer.java | 0 .../java/com/alibaba/datax/example/Main.java | 0 .../example/util/ExampleConfigParser.java | 5 +- .../alibaba/datax/example/util/PathUtil.java | 0 .../src/main/resources/example/conf/core.json | 0 .../datax/example/util/PathUtilTest.java | 19 +++ .../src/test/resources/pathTest.json | 1 + datax-example/datax-example-neo4j/pom.xml | 43 ++++++ .../neo4j/StreamReader2Neo4jWriterTest.java | 138 ++++++++++++++++++ .../test/resources/streamreader2neo4j.json | 51 +++++++ .../datax-example-streamreader/pom.xml | 37 +++++ .../StreamReader2StreamWriterTest.java | 2 +- .../src/test/resources}/stream2stream.json | 0 datax-example/doc/README.md | 81 +++------- datax-example/doc/img/img03.png | Bin 0 -> 43984 bytes datax-example/pom.xml | 17 +-- .../example/util/ExampleConfigParserTest.java | 33 ----- .../src/test/resources/example/conf/core.json | 60 -------- .../resources/job/notExistPluginTest.json | 36 ----- .../resources/job/stream/stream2stream.json | 36 ----- dataxPluginDev.md | 3 +- .../mock/StreamReader2Neo4jWriterTest.java | 59 -------- 24 files changed, 339 insertions(+), 307 deletions(-) create mode 100644 datax-example/datax-example-core/pom.xml rename datax-example/{ => datax-example-core}/src/main/java/com/alibaba/datax/example/ExampleContainer.java (100%) rename datax-example/{ => datax-example-core}/src/main/java/com/alibaba/datax/example/Main.java (100%) rename datax-example/{ => datax-example-core}/src/main/java/com/alibaba/datax/example/util/ExampleConfigParser.java (95%) rename datax-example/{ => datax-example-core}/src/main/java/com/alibaba/datax/example/util/PathUtil.java (100%) rename datax-example/{ => datax-example-core}/src/main/resources/example/conf/core.json (100%) create mode 100644 datax-example/datax-example-core/src/test/java/com/alibaba/datax/example/util/PathUtilTest.java create mode 100644 datax-example/datax-example-core/src/test/resources/pathTest.json create mode 100644 datax-example/datax-example-neo4j/pom.xml create mode 100644 datax-example/datax-example-neo4j/src/test/java/com/alibaba/datax/example/neo4j/StreamReader2Neo4jWriterTest.java create mode 100644 datax-example/datax-example-neo4j/src/test/resources/streamreader2neo4j.json create mode 100644 datax-example/datax-example-streamreader/pom.xml rename datax-example/{ => datax-example-streamreader}/src/test/java/com/alibaba/datax/example/streamreader/StreamReader2StreamWriterTest.java (89%) rename datax-example/{src/main/resources/job => datax-example-streamreader/src/test/resources}/stream2stream.json (100%) create mode 100644 datax-example/doc/img/img03.png delete mode 100644 datax-example/src/test/java/com/alibaba/datax/example/util/com/alibaba/datax/example/util/ExampleConfigParserTest.java delete mode 100755 datax-example/src/test/resources/example/conf/core.json delete mode 100644 datax-example/src/test/resources/job/notExistPluginTest.json delete mode 100644 datax-example/src/test/resources/job/stream/stream2stream.json delete mode 100644 neo4jwriter/src/test/java/com/alibaba/datax/plugin/writer/mock/StreamReader2Neo4jWriterTest.java diff --git a/README.md b/README.md index 54eb2203..315c72ac 100644 --- a/README.md +++ b/README.md @@ -100,10 +100,7 @@ DataX目前已经有了比较全面的插件体系,主流的RDBMS数据库、N - 整库迁移:https://help.aliyun.com/document_detail/137809.html - 批量上云:https://help.aliyun.com/document_detail/146671.html - 更新更多能力请访问:https://help.aliyun.com/document_detail/137663.html - - -# 本地快速调试读写插件定位BUG -在example模块可以方便快速的在本地运行任务 -点击:[datax-example使用](https://github.com/alibaba/DataX/datax-example/doc/README.md) + - # 我要开发新的插件 diff --git a/datax-example/datax-example-core/pom.xml b/datax-example/datax-example-core/pom.xml new file mode 100644 index 00000000..6a2e9e8e --- /dev/null +++ b/datax-example/datax-example-core/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + com.alibaba.datax + datax-example + 0.0.1-SNAPSHOT + + + datax-example-core + + + 8 + 8 + UTF-8 + + + \ No newline at end of file diff --git a/datax-example/src/main/java/com/alibaba/datax/example/ExampleContainer.java b/datax-example/datax-example-core/src/main/java/com/alibaba/datax/example/ExampleContainer.java similarity index 100% rename from datax-example/src/main/java/com/alibaba/datax/example/ExampleContainer.java rename to datax-example/datax-example-core/src/main/java/com/alibaba/datax/example/ExampleContainer.java diff --git a/datax-example/src/main/java/com/alibaba/datax/example/Main.java b/datax-example/datax-example-core/src/main/java/com/alibaba/datax/example/Main.java similarity index 100% rename from datax-example/src/main/java/com/alibaba/datax/example/Main.java rename to datax-example/datax-example-core/src/main/java/com/alibaba/datax/example/Main.java diff --git a/datax-example/src/main/java/com/alibaba/datax/example/util/ExampleConfigParser.java b/datax-example/datax-example-core/src/main/java/com/alibaba/datax/example/util/ExampleConfigParser.java similarity index 95% rename from datax-example/src/main/java/com/alibaba/datax/example/util/ExampleConfigParser.java rename to datax-example/datax-example-core/src/main/java/com/alibaba/datax/example/util/ExampleConfigParser.java index 7a24aaf4..6bbb4a23 100644 --- a/datax-example/src/main/java/com/alibaba/datax/example/util/ExampleConfigParser.java +++ b/datax-example/datax-example-core/src/main/java/com/alibaba/datax/example/util/ExampleConfigParser.java @@ -63,9 +63,8 @@ public class ExampleConfigParser { " **/*.*\n" + " \n" + " true\n" + - " \n [可参阅streamreader pom文件] \n" + - "3:If you are using 'datax-example' as the startup module, " + - "check whether the 'example' module has imported the dependencies of the plugin. Refer to the 'pom' file of the 'example' module"; + " \n [Refer to the streamreader pom file] \n" + + "3: Check that the datax-yourPlugin-example module imported your test plugin"; message = String.format(message, failedPlugin); throw DataXException.asDataXException(FrameworkErrorCode.PLUGIN_INIT_ERROR, message); } diff --git a/datax-example/src/main/java/com/alibaba/datax/example/util/PathUtil.java b/datax-example/datax-example-core/src/main/java/com/alibaba/datax/example/util/PathUtil.java similarity index 100% rename from datax-example/src/main/java/com/alibaba/datax/example/util/PathUtil.java rename to datax-example/datax-example-core/src/main/java/com/alibaba/datax/example/util/PathUtil.java diff --git a/datax-example/src/main/resources/example/conf/core.json b/datax-example/datax-example-core/src/main/resources/example/conf/core.json similarity index 100% rename from datax-example/src/main/resources/example/conf/core.json rename to datax-example/datax-example-core/src/main/resources/example/conf/core.json diff --git a/datax-example/datax-example-core/src/test/java/com/alibaba/datax/example/util/PathUtilTest.java b/datax-example/datax-example-core/src/test/java/com/alibaba/datax/example/util/PathUtilTest.java new file mode 100644 index 00000000..8985b54c --- /dev/null +++ b/datax-example/datax-example-core/src/test/java/com/alibaba/datax/example/util/PathUtilTest.java @@ -0,0 +1,19 @@ +package com.alibaba.datax.example.util; + +import org.junit.Assert; +import org.junit.Test; + +/** + * {@code Author} FuYouJ + * {@code Date} 2023/8/19 21:38 + */ + +public class PathUtilTest { + + @Test + public void testParseClassPathFile() { + String path = "/pathTest.json"; + String absolutePathFromClassPath = PathUtil.getAbsolutePathFromClassPath(path); + Assert.assertNotNull(absolutePathFromClassPath); + } +} diff --git a/datax-example/datax-example-core/src/test/resources/pathTest.json b/datax-example/datax-example-core/src/test/resources/pathTest.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/datax-example/datax-example-core/src/test/resources/pathTest.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/datax-example/datax-example-neo4j/pom.xml b/datax-example/datax-example-neo4j/pom.xml new file mode 100644 index 00000000..303b14a8 --- /dev/null +++ b/datax-example/datax-example-neo4j/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + com.alibaba.datax + datax-example + 0.0.1-SNAPSHOT + + + datax-example-neo4j + + + 8 + 8 + UTF-8 + 1.17.6 + 4.4.9 + + + + com.alibaba.datax + datax-example-core + 0.0.1-SNAPSHOT + + + org.testcontainers + testcontainers + ${test.container.version} + + + com.alibaba.datax + neo4jwriter + 0.0.1-SNAPSHOT + + + com.alibaba.datax + datax-example-streamreader + 0.0.1-SNAPSHOT + + + \ No newline at end of file diff --git a/datax-example/datax-example-neo4j/src/test/java/com/alibaba/datax/example/neo4j/StreamReader2Neo4jWriterTest.java b/datax-example/datax-example-neo4j/src/test/java/com/alibaba/datax/example/neo4j/StreamReader2Neo4jWriterTest.java new file mode 100644 index 00000000..9cf01253 --- /dev/null +++ b/datax-example/datax-example-neo4j/src/test/java/com/alibaba/datax/example/neo4j/StreamReader2Neo4jWriterTest.java @@ -0,0 +1,138 @@ +package com.alibaba.datax.example.neo4j; + +import com.alibaba.datax.example.ExampleContainer; +import com.alibaba.datax.example.util.PathUtil; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.neo4j.driver.*; +import org.neo4j.driver.types.Node; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.lifecycle.Startables; +import org.testcontainers.shaded.org.awaitility.Awaitility; +import org.testcontainers.utility.DockerImageName; +import org.testcontainers.utility.DockerLoggerFactory; + +import java.net.URI; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +/** + * {@code Author} FuYouJ + * {@code Date} 2023/8/19 21:48 + */ + +public class StreamReader2Neo4jWriterTest { + private static final Logger LOGGER = LoggerFactory.getLogger(StreamReader2Neo4jWriterTest.class); + private static final String CONTAINER_IMAGE = "neo4j:5.9.0"; + + private static final String CONTAINER_HOST = "neo4j-host"; + private static final int HTTP_PORT = 7474; + private static final int BOLT_PORT = 7687; + private static final String CONTAINER_NEO4J_USERNAME = "neo4j"; + private static final String CONTAINER_NEO4J_PASSWORD = "Test@12343"; + private static final URI CONTAINER_URI = URI.create("neo4j://localhost:" + BOLT_PORT); + + protected static final Network NETWORK = Network.newNetwork(); + + private GenericContainer container; + protected Driver neo4jDriver; + protected Session neo4jSession; + private static final int CHANNEL = 5; + private static final int READER_NUM = 10; + + @Before + public void init() { + DockerImageName imageName = DockerImageName.parse(CONTAINER_IMAGE); + container = + new GenericContainer<>(imageName) + .withNetwork(NETWORK) + .withNetworkAliases(CONTAINER_HOST) + .withExposedPorts(HTTP_PORT, BOLT_PORT) + .withEnv( + "NEO4J_AUTH", + CONTAINER_NEO4J_USERNAME + "/" + CONTAINER_NEO4J_PASSWORD) + .withEnv("apoc.export.file.enabled", "true") + .withEnv("apoc.import.file.enabled", "true") + .withEnv("apoc.import.file.use_neo4j_config", "true") + .withEnv("NEO4J_PLUGINS", "[\"apoc\"]") + .withLogConsumer( + new Slf4jLogConsumer( + DockerLoggerFactory.getLogger(CONTAINER_IMAGE))); + container.setPortBindings( + Arrays.asList( + String.format("%s:%s", HTTP_PORT, HTTP_PORT), + String.format("%s:%s", BOLT_PORT, BOLT_PORT))); + Startables.deepStart(Stream.of(container)).join(); + LOGGER.info("container started"); + Awaitility.given() + .ignoreExceptions() + .await() + .atMost(30, TimeUnit.SECONDS) + .untilAsserted(this::initConnection); + } + + //在neo4jWriter模块使用Example测试整个job,方便发现整个流程的代码问题 + @Test + public void streamReader2Neo4j() { + + deleteHistoryIfExist(); + + String path = "/streamreader2neo4j.json"; + String jobPath = PathUtil.getAbsolutePathFromClassPath(path); + + ExampleContainer.start(jobPath); + + //根据channel和reader的mock数据,校验结果集是否符合预期 + verifyWriteResult(); + } + + private void deleteHistoryIfExist() { + String query = "match (n:StreamReader) return n limit 1"; + String delete = "match (n:StreamReader) delete n"; + if (neo4jSession.run(query).hasNext()) { + neo4jSession.run(delete); + } + } + + private void verifyWriteResult() { + int total = CHANNEL * READER_NUM; + String query = "match (n:StreamReader) return n"; + Result run = neo4jSession.run(query); + int count = 0; + while (run.hasNext()) { + Record record = run.next(); + Node node = record.get("n").asNode(); + if (node.hasLabel("StreamReader")) { + count++; + } + } + Assert.assertEquals(count, total); + } + @After + public void destroy() { + if (neo4jSession != null) { + neo4jSession.close(); + } + if (neo4jDriver != null) { + neo4jDriver.close(); + } + if (container != null) { + container.close(); + } + } + + private void initConnection() { + neo4jDriver = + GraphDatabase.driver( + CONTAINER_URI, + AuthTokens.basic(CONTAINER_NEO4J_USERNAME, CONTAINER_NEO4J_PASSWORD)); + neo4jSession = neo4jDriver.session(SessionConfig.forDatabase("neo4j")); + } +} diff --git a/datax-example/datax-example-neo4j/src/test/resources/streamreader2neo4j.json b/datax-example/datax-example-neo4j/src/test/resources/streamreader2neo4j.json new file mode 100644 index 00000000..3d543ce3 --- /dev/null +++ b/datax-example/datax-example-neo4j/src/test/resources/streamreader2neo4j.json @@ -0,0 +1,51 @@ +{ + "job": { + "content": [ + { + "reader": { + "name": "streamreader", + "parameter": { + "sliceRecordCount": 10, + "column": [ + { + "type": "string", + "value": "StreamReader" + }, + { + "type": "string", + "value": "1997" + } + ] + } + }, + "writer": { + "name": "neo4jWriter", + "parameter": { + "uri": "bolt://localhost:7687", + "username":"neo4j", + "password":"Test@12343", + "database":"neo4j", + "cypher": "unwind $batch as row CALL apoc.cypher.doIt( 'create (n:`' + row.Label + '`{id:$id})' ,{id: row.id} ) YIELD value RETURN 1 ", + "batchDataVariableName": "batch", + "batchSize": "3", + "properties": [ + { + "name": "Label", + "type": "string" + }, + { + "name": "id", + "type": "STRING" + } + ] + } + } + } + ], + "setting": { + "speed": { + "channel": 5 + } + } + } +} \ No newline at end of file diff --git a/datax-example/datax-example-streamreader/pom.xml b/datax-example/datax-example-streamreader/pom.xml new file mode 100644 index 00000000..ea70de10 --- /dev/null +++ b/datax-example/datax-example-streamreader/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + com.alibaba.datax + datax-example + 0.0.1-SNAPSHOT + + + datax-example-streamreader + + + 8 + 8 + UTF-8 + + + + com.alibaba.datax + datax-example-core + 0.0.1-SNAPSHOT + + + com.alibaba.datax + streamreader + 0.0.1-SNAPSHOT + + + com.alibaba.datax + streamwriter + 0.0.1-SNAPSHOT + + + + \ No newline at end of file diff --git a/datax-example/src/test/java/com/alibaba/datax/example/streamreader/StreamReader2StreamWriterTest.java b/datax-example/datax-example-streamreader/src/test/java/com/alibaba/datax/example/streamreader/StreamReader2StreamWriterTest.java similarity index 89% rename from datax-example/src/test/java/com/alibaba/datax/example/streamreader/StreamReader2StreamWriterTest.java rename to datax-example/datax-example-streamreader/src/test/java/com/alibaba/datax/example/streamreader/StreamReader2StreamWriterTest.java index 8081677e..71d083d0 100644 --- a/datax-example/src/test/java/com/alibaba/datax/example/streamreader/StreamReader2StreamWriterTest.java +++ b/datax-example/datax-example-streamreader/src/test/java/com/alibaba/datax/example/streamreader/StreamReader2StreamWriterTest.java @@ -12,7 +12,7 @@ import org.junit.Test; public class StreamReader2StreamWriterTest { @Test public void testStreamReader2StreamWriter() { - String path = "/job/stream/stream2stream.json"; + String path = "/stream2stream.json"; String jobPath = PathUtil.getAbsolutePathFromClassPath(path); ExampleContainer.start(jobPath); } diff --git a/datax-example/src/main/resources/job/stream2stream.json b/datax-example/datax-example-streamreader/src/test/resources/stream2stream.json similarity index 100% rename from datax-example/src/main/resources/job/stream2stream.json rename to datax-example/datax-example-streamreader/src/test/resources/stream2stream.json diff --git a/datax-example/doc/README.md b/datax-example/doc/README.md index d44e10d2..15f77e87 100644 --- a/datax-example/doc/README.md +++ b/datax-example/doc/README.md @@ -19,6 +19,10 @@ img +### 目录结构 +该目录结构演示了如何使用datax-example-core编写测试用例,和校验代码流程。 +img + ### 实现原理 - 不修改原有的ConfigParer,使用新的ExampleConfigParser,仅用于example模块。他不依赖datax.home,而是依赖ide编译后的target目录 @@ -70,46 +74,23 @@ ``` -#### 在example模块使用 -1.在datax-example模块引入你需要的插件,默认只引入了streamreader、writer - -2.打开datax-example的Main class - +#### 在测试模块模块使用 +参考datax-example/datax-example-streamreader的StreamReader2StreamWriterTest.java ```java -public class Main { - - /** - * 注意! - * 1.在example模块pom文件添加你依赖的的调试插件, - * 你可以直接打开本模块的pom文件,参考是如何引入streamreader,streamwriter - * 2. 在此处指定你的job文件 - */ - public static void main(String[] args) { - - String classPathJobPath = "/job/stream2stream.json"; - String absJobPath = PathUtil.getAbsolutePathFromClassPath(classPathJobPath); - startExample(absJobPath); - } - - public static void startExample(String jobPath) { - - Configuration configuration = ExampleConfigParser.parse(jobPath); - - Engine engine = new Engine(); - engine.start(configuration); - } - -} -``` -#### 在reader/writer模块使用 -参考neo4jwriter的StreamReader2Neo4jWriterTest -```java -public class StreamReader2Neo4jWriterTest extends Neo4jWriterTest { - private static final int CHANNEL = 5; - private static final int READER_NUM = 10; - - //在neo4jWriter模块使用Example测试整个job,方便发现整个流程的代码问题 +public class StreamReader2StreamWriterTest { @Test + public void testStreamReader2StreamWriter() { + String path = "/stream2stream.json"; + String jobPath = PathUtil.getAbsolutePathFromClassPath(path); + ExampleContainer.start(jobPath); + } +} + +``` +参考datax-example/datax-example-neo4j的StreamReader2Neo4jWriterTest +```java +public class StreamReader2Neo4jWriterTest{ +@Test public void streamReader2Neo4j() { deleteHistoryIfExist(); @@ -122,29 +103,5 @@ public class StreamReader2Neo4jWriterTest extends Neo4jWriterTest { //根据channel和reader的mock数据,校验结果集是否符合预期 verifyWriteResult(); } - - private void deleteHistoryIfExist() { - String query = "match (n:StreamReader) return n limit 1"; - String delete = "match (n:StreamReader) delete n"; - if (super.neo4jSession.run(query).hasNext()) { - neo4jSession.run(delete); - } - } - - private void verifyWriteResult() { - int total = CHANNEL * READER_NUM; - String query = "match (n:StreamReader) return n"; - Result run = neo4jSession.run(query); - int count = 0; - while (run.hasNext()) { - Record record = run.next(); - Node node = record.get("n").asNode(); - if (node.hasLabel("StreamReader")) { - count++; - } - } - Assert.assertEquals(count, total); - } } - ``` \ No newline at end of file diff --git a/datax-example/doc/img/img03.png b/datax-example/doc/img/img03.png new file mode 100644 index 0000000000000000000000000000000000000000..731f81bde0190850c97615b2628d93a86b36a0b0 GIT binary patch literal 43984 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 literal 0 HcmV?d00001 diff --git a/datax-example/pom.xml b/datax-example/pom.xml index 17bb9e18..9c4c9200 100644 --- a/datax-example/pom.xml +++ b/datax-example/pom.xml @@ -10,6 +10,12 @@ datax-example + pom + + datax-example-core + datax-example-streamreader + datax-example-neo4j + 8 @@ -28,17 +34,6 @@ datax-core 0.0.1-SNAPSHOT - - - com.alibaba.datax - streamwriter - 0.0.1-SNAPSHOT - - - com.alibaba.datax - streamreader - 0.0.1-SNAPSHOT - junit junit diff --git a/datax-example/src/test/java/com/alibaba/datax/example/util/com/alibaba/datax/example/util/ExampleConfigParserTest.java b/datax-example/src/test/java/com/alibaba/datax/example/util/com/alibaba/datax/example/util/ExampleConfigParserTest.java deleted file mode 100644 index 5a99e7b1..00000000 --- a/datax-example/src/test/java/com/alibaba/datax/example/util/com/alibaba/datax/example/util/ExampleConfigParserTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.alibaba.datax.example.util.com.alibaba.datax.example.util; - -import com.alibaba.datax.common.util.Configuration; -import com.alibaba.datax.example.util.ExampleConfigParser; -import com.alibaba.datax.example.util.PathUtil; -import org.junit.Assert; -import org.junit.Test; - -import java.io.File; - - -public class ExampleConfigParserTest { - - - @Test - public void testExampleConfigParserShouldLoadDefaultConf() { - - String path = "/job/stream2stream.json"; - Configuration testConfiguration = ExampleConfigParser.parse( - PathUtil.getAbsolutePathFromClassPath(path) - ); - Configuration defaultConf = loadDefaultConf(); - Assert.assertEquals(testConfiguration.get("core"), defaultConf.get("core")); - Assert.assertEquals(testConfiguration.get("common"), defaultConf.get("common")); - } - - private Configuration loadDefaultConf() { - return Configuration.from( - new File(PathUtil.getAbsolutePathFromClassPath("/example/conf/core.json") - ) - ); - } -} diff --git a/datax-example/src/test/resources/example/conf/core.json b/datax-example/src/test/resources/example/conf/core.json deleted file mode 100755 index 33281ac0..00000000 --- a/datax-example/src/test/resources/example/conf/core.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "entry": { - "jvm": "-Xms1G -Xmx1G", - "environment": {} - }, - "common": { - "column": { - "datetimeFormat": "yyyy-MM-dd HH:mm:ss", - "timeFormat": "HH:mm:ss", - "dateFormat": "yyyy-MM-dd", - "extraFormats":["yyyyMMdd"], - "timeZone": "GMT+8", - "encoding": "utf-8" - } - }, - "core": { - "dataXServer": { - "address": "http://localhost:7001/api", - "timeout": 10000, - "reportDataxLog": false, - "reportPerfLog": false - }, - "transport": { - "channel": { - "class": "com.alibaba.datax.core.transport.channel.memory.MemoryChannel", - "speed": { - "byte": -1, - "record": -1 - }, - "flowControlInterval": 20, - "capacity": 512, - "byteCapacity": 67108864 - }, - "exchanger": { - "class": "com.alibaba.datax.core.plugin.BufferedRecordExchanger", - "bufferSize": 32 - } - }, - "container": { - "job": { - "reportInterval": 10000 - }, - "taskGroup": { - "channel": 5 - }, - "trace": { - "enable": "false" - } - - }, - "statistics": { - "collector": { - "plugin": { - "taskClass": "com.alibaba.datax.core.statistics.plugin.task.StdoutPluginCollector", - "maxDirtyNumber": 10 - } - } - } - } -} diff --git a/datax-example/src/test/resources/job/notExistPluginTest.json b/datax-example/src/test/resources/job/notExistPluginTest.json deleted file mode 100644 index afefaad3..00000000 --- a/datax-example/src/test/resources/job/notExistPluginTest.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "job": { - "content": [ - { - "reader": { - "name": "notExistReaderPlugin", - "parameter": { - "sliceRecordCount": 10, - "column": [ - { - "type": "long", - "value": "10" - }, - { - "type": "string", - "value": "hello,你好,世界-DataX" - } - ] - } - }, - "writer": { - "name": "streamwriter", - "parameter": { - "encoding": "UTF-8", - "print": true - } - } - } - ], - "setting": { - "speed": { - "channel": 5 - } - } - } -} \ No newline at end of file diff --git a/datax-example/src/test/resources/job/stream/stream2stream.json b/datax-example/src/test/resources/job/stream/stream2stream.json deleted file mode 100644 index b2a57395..00000000 --- a/datax-example/src/test/resources/job/stream/stream2stream.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "job": { - "content": [ - { - "reader": { - "name": "streamreader", - "parameter": { - "sliceRecordCount": 10, - "column": [ - { - "type": "long", - "value": "10" - }, - { - "type": "string", - "value": "hello,你好,世界-DataX" - } - ] - } - }, - "writer": { - "name": "streamwriter", - "parameter": { - "encoding": "UTF-8", - "print": true - } - } - } - ], - "setting": { - "speed": { - "channel": 5 - } - } - } -} \ No newline at end of file diff --git a/dataxPluginDev.md b/dataxPluginDev.md index 98aa41a3..098bf819 100644 --- a/dataxPluginDev.md +++ b/dataxPluginDev.md @@ -448,8 +448,7 @@ DataX的内部类型在实现上会选用不同的java类型: 4. 根据插件配置中定义的入口类,框架通过反射实例化对应的`Job`和`Task`对象。 ### 编写测试用例 -1. 你可以在你的插件模块引入datax-example模块,调用`ExampleContainer.start(jobPath)`方法来检测你的代码逻辑是否正确。[datax-example使用](https://github.com/alibaba/DataX/datax-example/doc/README.md) -2. 你可以在datax-example模块引入你的插件模块,编写测试方法调用`ExampleContainer.start(jobPath)`方法来检测你的代码逻辑是否正确。[datax-example使用](https://github.com/alibaba/DataX/datax-example/doc/README.md) +1. 在datax-example工程下新建新的插件测试模块,调用`ExampleContainer.start(jobPath)`方法来检测你的代码逻辑是否正确。[datax-example使用](https://github.com/alibaba/DataX/datax-example/doc/README.md) ## 三、Last but not Least diff --git a/neo4jwriter/src/test/java/com/alibaba/datax/plugin/writer/mock/StreamReader2Neo4jWriterTest.java b/neo4jwriter/src/test/java/com/alibaba/datax/plugin/writer/mock/StreamReader2Neo4jWriterTest.java deleted file mode 100644 index 3335f5e7..00000000 --- a/neo4jwriter/src/test/java/com/alibaba/datax/plugin/writer/mock/StreamReader2Neo4jWriterTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.alibaba.datax.plugin.writer.mock; - -import com.alibaba.datax.example.ExampleContainer; -import com.alibaba.datax.example.util.PathUtil; -import com.alibaba.datax.plugin.writer.Neo4jWriterTest; -import org.junit.Assert; -import org.junit.Test; -import org.neo4j.driver.Record; -import org.neo4j.driver.Result; -import org.neo4j.driver.types.Node; - -/** - * 展示如何使用ExampleContainer运行测试用例 - * {@code Author} FuYouJ - * {@code Date} 2023/8/6 11:36 - */ - -public class StreamReader2Neo4jWriterTest extends Neo4jWriterTest { - private static final int CHANNEL = 5; - private static final int READER_NUM = 10; - - //在neo4jWriter模块使用Example测试整个job,方便发现整个流程的代码问题 - @Test - public void streamReader2Neo4j() { - - deleteHistoryIfExist(); - - String path = "/streamreader2neo4j.json"; - String jobPath = PathUtil.getAbsolutePathFromClassPath(path); - - ExampleContainer.start(jobPath); - - //根据channel和reader的mock数据,校验结果集是否符合预期 - verifyWriteResult(); - } - - private void deleteHistoryIfExist() { - String query = "match (n:StreamReader) return n limit 1"; - String delete = "match (n:StreamReader) delete n"; - if (super.neo4jSession.run(query).hasNext()) { - neo4jSession.run(delete); - } - } - - private void verifyWriteResult() { - int total = CHANNEL * READER_NUM; - String query = "match (n:StreamReader) return n"; - Result run = neo4jSession.run(query); - int count = 0; - while (run.hasNext()) { - Record record = run.next(); - Node node = record.get("n").asNode(); - if (node.hasLabel("StreamReader")) { - count++; - } - } - Assert.assertEquals(count, total); - } -}