回归测试

    1. : 一个测试用例,目前仅用来指代测试用例文件名
    2. Group: 一个测试集,目前仅用于指代测试用例所属的目录
    3. Action: 一个封装好的具体测试行为,比如用于执行sql的sql Action,用于校验结果的test Action,用于导入数据的streamLoad Action等

    测试步骤

    1. 需要预先安装好集群
    2. 修改配置文件${DORIS_HOME}/conf/regression-conf.groovy,设置jdbc url、用户等配置项
    3. 创建测试用例文件并编写用例
    4. 如果用例文件包含qt Action,则需要创建关联的的data文件,比如suites/demo/qt_action.groovy这个例子,需要用到data/demo/qt_action.out这个TSV文件来校验输出是否一致
    5. 运行${DORIS_HOME}/run-regression-test.sh测试全部用例,或运行${DORIS_HOME}/run-regression-test.sh --run <suiteName> 测试若干用例,更多例子见”启动脚本例子”章节
    1. run-regression-test.sh: 启动脚本
    2. regression-conf.groovy: 回归测试的默认配置
    3. data: 存放输入数据和输出校验数据
    4. suites: 存放用例

    框架默认配置

    测试时需要实际情况修改jdbc和fe的配置

    1. /* ============ 一般只需要关注下面这部分 ============ */
    2. // 默认DB,如果未创建,则会尝试创建这个db
    3. defaultDb = "regression_test"
    4. // Jdbc配置
    5. jdbcUrl = "jdbc:mysql://127.0.0.1:9030/?"
    6. jdbcUser = "root"
    7. jdbcPassword = ""
    8. // fe地址配置, 用于stream load
    9. feHttpAddress = "127.0.0.1:8030"
    10. feHttpUser = "root"
    11. feHttpPassword = ""
    12. /* ============ 一般不需要修改下面的部分 ============ */
    13. // DORIS_HOME变量是通过run-regression-test.sh传入的
    14. // 即 java -DDORIS_HOME=./
    15. // 设置回归测试用例的目录
    16. suitePath = "${DORIS_HOME}/regression-test/suites"
    17. // 设置输入输出数据的目录
    18. dataPath = "${DORIS_HOME}/regression-test/data"
    19. // 默认会读所有的组,读多个组可以用半角逗号隔开,如: "demo,performance"
    20. // 一般不需要在配置文件中修改,而是通过run-regression-test.sh来动态指定和覆盖
    21. testGroups = ""
    22. // 默认会读所有的用例, 同样可以使用run-regression-test.sh来动态指定和覆盖
    23. testSuites = ""
    24. // 其他自定义配置
    25. customConf1 = "test_custom_conf_value"
    1. 进入${DORIS_HOME}/regression-test目录
    2. 根据测试的目的来选择用例的目录,正确性测试存在suites/correctness,而性能测试存在suites/performance
    3. 新建一个groovy用例文件,增加若干Action用于测试,Action讲在后续章节具体说明

    Action

    Action是一个测试框架默认提供的测试行为,使用DSL来定义。

    sql action用于提交sql并获取结果,如果查询失败则会抛出异常

    参数如下

    • String sql: 输入的sql字符串
    • return List<List>: 查询结果,如果是DDL/DML,则返回一行一列,唯一的值是updateRowCount
    1. // execute sql and ignore result
    2. sql "show databases"
    3. // execute sql and get result, outer List denote rows, inner List denote columns in a single row
    4. List<List<Object>> tables = sql "show tables"
    5. // assertXxx() will invoke junit5's Assertions.assertXxx() dynamically
    6. assertTrue(tables.size() >= 0) // test rowCount >= 0
    7. // syntax error
    8. try {
    9. sql "a b c d e"
    10. throw new IllegalStateException("Should be syntax error")
    11. } catch (java.sql.SQLException t) {
    12. assertTrue(true)
    13. }
    14. def testTable = "test_sql_action1"
    15. try {
    16. sql "DROP TABLE IF EXISTS ${testTable}"
    17. // multi-line sql
    18. def result1 = sql """
    19. CREATE TABLE IF NOT EXISTS ${testTable} (
    20. id int
    21. )
    22. DISTRIBUTED BY HASH(id) BUCKETS 1
    23. PROPERTIES (
    24. "replication_num" = "1"
    25. )
    26. """
    27. // DDL/DML return 1 row and 1 column, the only value is update row count
    28. assertTrue(result1[0].size() == 1)
    29. assertTrue(result1[0][0] == 0, "Create table should update 0 rows")
    30. def result2 = sql "INSERT INTO test_sql_action1 values(1), (2), (3)"
    31. assertTrue(result2.size() == 1)
    32. assertTrue(result2[0].size() == 1)
    33. assertTrue(result2[0][0] == 3, "Insert should update 3 rows")
    34. } finally {
    35. /**
    36. * try_xxx(args) means:
    37. *
    38. * try {
    39. * return xxx(args)
    40. * } catch (Throwable t) {
    41. * // do nothing
    42. * }
    43. */
    44. try_sql("DROP TABLE IF EXISTS ${testTable}")
    45. // you can see the error sql will not throw exception and return
    46. try {
    47. def errorSqlResult = try_sql("a b c d e f g")
    48. assertTrue(errorSqlResult == null)
    49. } catch (Throwable t) {
    50. assertTrue(false, "Never catch exception")
    51. }
    52. }
    53. // order_sql(sqlStr) equals to sql(sqlStr, isOrder=true)
    54. // sort result by string dict
    55. def list = order_sql """
    56. select 2
    57. union all
    58. select 1
    59. union all
    60. select null
    61. union all
    62. select 15
    63. union all
    64. select 3
    65. """
    66. assertEquals(null, list[0][0])
    67. assertEquals(1, list[1][0])
    68. assertEquals(15, list[2][0])
    69. assertEquals(2, list[3][0])
    70. assertEquals(3, list[4][0])

    qt action用于提交sql,并使用对应的.out TSV文件来校验结果

    • String sql: 输入sql字符串
    • return void

    下面的样例代码存放于${DORIS_HOME}/regression-test/suites/demo/qt_action.groovy:

    test action可以使用更复杂的校验规则来测试,比如验证行数、执行时间、是否抛出异常

    可用参数

    • String sql: 输入的sql字符串
    • List<List> result: 提供一个List对象,用于校验真实查询结果对比是否相等
    • String exception: 校验抛出的异常是否包含某些字符串
    • long rowNum: 验证结果行数
    • long time: 验证执行时间是否小于这个值,单位是毫秒
    • Closure<List<List>, Throwable, Long, Long> check: 自定义回调校验,可传入结果、异常、时间。存在回调函数时,其他校验方式会失效。
    1. test {
    2. sql "abcdefg"
    3. // check exception message contains
    4. exception "errCode = 2, detailMessage = Syntax error"
    5. }
    6. test {
    7. sql """
    8. select *
    9. from (
    10. select 1 id
    11. union all
    12. select 2
    13. ) a
    14. order by id"""
    15. // multi check condition
    16. // check return 2 rows
    17. rowNum 2
    18. // execute time must <= 5000 millisecond
    19. time 5000
    20. // check result, must be 2 rows and 1 column, the first row is 1, second is 2
    21. result(
    22. [[1], [2]]
    23. )
    24. }
    25. test {
    26. sql "a b c d e f g"
    27. // other check will not work because already declared a check callback
    28. exception "aaaaaaaaa"
    29. // callback
    30. check { result, exception, startTime, endTime ->
    31. // assertXxx() will invoke junit5's Assertions.assertXxx() dynamically
    32. assertTrue(exception != null)
    33. }
    34. test {
    35. sql """
    36. union all
    37. select 1
    38. union all
    39. select null
    40. union all
    41. select 15
    42. union all
    43. select 3
    44. """
    45. check { result, ex, startTime, endTime ->
    46. // same as order_sql(sqlStr)
    47. result = sortRows(result)
    48. assertEquals(null, result[0][0])
    49. assertEquals(1, result[1][0])
    50. assertEquals(15, result[2][0])
    51. assertEquals(2, result[3][0])
    52. assertEquals(3, result[4][0])
    53. }
    54. }

    explain action用来校验explain返回的字符串是否包含某些字符串

    可用参数:

    • String sql: 查询的sql,需要去掉sql中的explain
    • String contains: 校验explain是否包含某些字符串,可多次调用校验同时多个结果
    • String notContains: 校验explain是否不含某些字符串,可多次调用校验同时多个结果
    • Closure check: 自定义校验回调函数,可以获取返回的字符串,存在校验函数时,其他校验方式会失效
    • Closure<String, Throwable, Long, Long> check: 自定义校验回调函数,可以额外获取异常和时间

    下面的样例代码存放于${DORIS_HOME}/regression-test/suites/demo/explain_action.groovy:

    1. explain {
    2. sql("select 100")
    3. // contains("OUTPUT EXPRS:<slot 0> 100\n") && contains("PARTITION: UNPARTITIONED\n")
    4. contains "OUTPUT EXPRS:<slot 0> 100\n"
    5. contains "PARTITION: UNPARTITIONED\n"
    6. }
    7. explain {
    8. sql("select 100")
    9. // contains(" 100\n") && !contains("abcdefg") && !("1234567")
    10. contains " 100\n"
    11. notContains "abcdefg"
    12. notContains "1234567"
    13. }
    14. explain {
    15. sql("select 100")
    16. // simple callback
    17. check { explainStr -> explainStr.contains("abcdefg") || explainStr.contains(" 100\n") }
    18. }
    19. explain {
    20. sql("a b c d e")
    21. // callback with exception and time
    22. check { explainStr, exception, startTime, endTime ->
    23. // assertXxx() will invoke junit5's Assertions.assertXxx() dynamically
    24. assertTrue(exception != null)
    25. }
    26. }

    streamLoad action用于导入数据 可用参数为

    • String db: db,默认值为regression-conf.groovy中的defaultDb
    • String table: 表名
    • String file: 要导入的文件路径,可以写data目录下的相对路径,或者写http url来导入网络文件
    • Iterator<List> inputIterator: 要导入的迭代器
    • String inputText: 要导入的文本, 较为少用
    • InputStream inputStream: 要导入的字节流,较为少用
    • long time: 验证执行时间是否小于这个值,单位是毫秒
    • void set(String key, String value): 设置stream load的http请求的header,如label、columnSeparator
    • Closure<String, Throwable, Long, Long> check: 自定义校验回调函数,可以获取返回结果、异常和超时时间。当存在回调函数时,其他校验项会失效。
    1. # 查看脚本参数说明
    2. ./run-regression-test.sh h
    3. # 查看框架参数说明
    4. ./run-regression-test.sh --run -h
    5. # 测试所有用例
    6. ./run-regression-test.sh
    7. # 删除测试框架编译结果和测试日志
    8. ./run-regression-test.sh --clean
    9. # 测试suiteName为sql_action的用例, 目前suiteName等于文件名前缀,例子对应的用例文件是sql_action.groovy
    10. ./run-regression-test.sh --run sql_action
    11. # 测试suiteName包含'sql'的用例,**注意需要用单引号括起来**
    12. ./run-regression-test.sh --run '*sql*'
    13. # 测试demo和perfomance group
    14. ./run-regression-test.sh --run -g 'demo,performance'
    15. # 测试demo group下的sql_action
    16. ./run-regression-test.sh --run -g demo -s sql_action
    17. # 自定义配置
    18. ./run-regression-test.sh --run -conf a=b

    使用查询结果自动生成.out文件

    1. # 使用查询结果自动生成sql_action用例的.out文件,如果.out文件存在则忽略
    2. ./run-regression-test.sh --run sql_action -genOut
    3. ./run-regression-test.sh --run sql_action -forceGenOut