testthat 2.0.0のJunitReporterを使ってCircleCIでTest Summaryを表示する

CircleCIは、テスト結果をJUnitCucumberの形式で渡すとサマリをいい感じに表示してくれます。

なんだRとは縁のない話か、と思ってたら、testthat 2.0.0で、

New JunitReporter generates reports in JUnit compatible format. (https://github.com/r-lib/testthat/blob/cb5db4aed48c0e155ac9e9fe2709f61ee23c2c50/NEWS.md#reporters)

とのことなのでやってみました。

試しに作ってみたレポジトリはこれです:

※現時点(testthat 2.0.0未リリース)でこれを試すには、DESCRIPTIONRemotes: r-lib/testthatを指定してください。

Reporterの指定

testthatは、Reporterを指定することでテスト結果の出力を変えることができます。Reporterの種類は以下に記載があります。

どこに指定すればいいかというと、ドキュメントがうまく見つけられなかったのであってるか自信がないんですが、test_check()reporter引数みたいです。(set_reporter()でもいけそうだけどよくわかりませんでした...)

JunitReporterはR6クラスなので、new()で生成したインスタンスを渡します。new()に指定するのはfileという引数で、デフォルトだとstdout()になっています。スーパークラスReporterの定義はこう:

    initialize = function(file = getOption("testthat.output_file", stdout())) {
      self$out <- file
      if (is.character(self$out) && file.exists(self$out)) {
        # If writing to a file, overwrite it if it exists
        file.remove(self$out)
      }
    }

(https://github.com/r-lib/testthat/blob/cb5db4aed48c0e155ac9e9fe2709f61ee23c2c50/R/reporter.R#L24)

これに、拡張子が.xmlの適当なファイル名を指定します。

test_check("testthatJunitRporterTest",
           reporter = JunitReporter$new(file = "junit_result.xml"))

CircleCIの設定ファイル

JunitReporterによって吐き出されるファイルの場所にもよりますが、上の設定であればtestsディレクトリにjunit_result.xmlができるので、store_test_resultsにはを指定します。

    - store_test_results:
        path: /tmp/Rcheck/tests/
        when: always

全部書くとこんな感じです。(他の部分の説明はRのテストにCircleCIを使う - Technically, technophobic.に書いたのでそっちを見てください)

defaults: &steps
  steps:
    - checkout

    ## setup -------------------------------

    - run:
        name: Set environmental variables
        command: |
          Rscript --vanilla \
            -e 'dsc <- read.dcf("DESCRIPTION")' \
            -e 'cat(sprintf("export PKG_TARBALL=%s_%s.tar.gz\n", dsc[,"Package"], dsc[,"Version"]))' \
            -e 'cat(sprintf("export RCHECK_DIR=%s.Rcheck\n", dsc[,"Package"]))' \
            >> ${BASH_ENV}

    ## install dependencies ------------------

    - run:
        name: Install devtools and dependencies
        command: |
          Rscript \
            -e 'if (!requireNamespace("devtools", quietly = TRUE)) install.packages("devtools")' \
            -e 'devtools::install_deps(dependencies = TRUE)'

    ## build and test -----------------

    - run:
        name: Build package
        command: R CMD build .

    - run:
        name: Check package
        command: R CMD check "${PKG_TARBALL}" --as-cran --no-manual
    - run:
        name: Check failures
        command: |
          Rscript -e "message(devtools::check_failures(path = '${RCHECK_DIR}'))"
          # warnings are errors
          # - run: if grep -q -R "WARNING" "${RCHECK_DIR}/00check.log"; then exit 1; fi

    ## store artifacts -----------------

    - run:
        command: mv ${RCHECK_DIR} /tmp/Rcheck
        when: always
    - store_test_results:
        path: /tmp/Rcheck/tests/
        when: always
    - store_artifacts:
        path: /tmp/Rcheck
        when: always

version: 2
jobs:
  "r-release":
     docker:
       - image: rocker/tidyverse:latest
     <<: *steps

  "r-devel":
     docker:
       - image: rocker/tidyverse:devel
     <<: *steps

workflows:
  version: 2
  build_and_test:
    jobs:
      - "r-release"
      - "r-devel"

結果

こうなりました。わかりやすくていいですね。

f:id:yutannihilation:20171018182347p:plain

https://circleci.com/gh/yutannihilation/testthatJunitRporterTest/25