5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Jenkins] Pipelineスクリプトの単体テスト環境構築

Last updated at Posted at 2021-09-12

はじめに

業務で、JenkinsのPipelineスクリプト(いわゆるJenkinsfileというファイル名の、Groovyスクリプト)を書くことが度々あります。その単体テストができるということで
Jenkins Pipeline Unit testing frameworkというものを使ってみようとしたのですが。。。
筆者の知識が十分でなかったために、休日を1日溶かしました。

  • Javaでの開発経験ならあるが、機能追加・不具合対応ばかりなので、既存のコードについて完璧には知らない
  • 今回は、JUnitのアノテーション(C#でいう属性のような、関数定義の冒頭につけるアレ)を思い出せなかった

そこで、同じような目に会う方が減ってくれるといいな、という思いで投稿します。
今更Jenkins、という方もいるでしょう。しかし、組み込みソフト開発の業界は他のソフト開発業界での流行が、数年遅れて来ると思っているので、少しはニーズがあると嬉しいです。

構築手順

ハマったことも添えながら、手順を示していきます。
事前準備のところは、ネットに転がっているので、省略します。
(DOS窓で、java --versionとかgradle -vとか打って、返事もらえていればOK)

  • 事前準備

    • JDKのインストール
    • Gradleのインストール
  • 手順概要

    1. ルートフォルダを作成して、gradle initする
    2. ソース格納フォルダを作成して、build.gradleを編集する
    3. テスト対象コードを作成する
    4. テストコードを作成する
    5. テストを実行する

1. ルートフォルダを作成して、gradle initする

好きなところに、好きな名前のフォルダを作成してください。
(以降の例は、C:/Work/pipelineUnitTestで進めます)

作成したフォルダを表示するWindowsエクスプローラのアドレス欄にcmdと打つなどして、作成したフォルダがカレントのDOS窓を開いて、gradle initと打ちます。
(以下は、Gradle 7.2での実行例。途中のキー操作は、何も入力せずにEnterでもOKです。)

C:\Work\pipelineUnitTest>gradle init

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 1

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 1

Project name (default: pipelineUnitTest):


> Task :init
Get more help with your project: Learn more about Gradle by exploring our samples
 at https://docs.gradle.org/7.2/samples

BUILD SUCCESSFUL in 1m 39s
2 actionable tasks: 2 executed
C:\Work\pipelineUnitTest>

いきなり選択肢が4個ある時点で、面食らいます。
普通に最初は2: applicationを選択したため、生成されたファイルが多すぎる上、どこに何が書いてあるかよく分からない、という状態に陥りました。
2: applicationを選択して生成されるbuild.gradleは次の手順2.で利用できます)

2. ソース格納フォルダを作成して、build.gradleを編集する

ルートフォルダ以下に、任意のソース格納フォルダを作成します。
GradleとかJavaとかの作法、お決まりみたいなのを知らないので、
筆者は下記srcフォルダ以下を適当に作成しました。

pipelineUnitTest
│
├─.gradle       // 自動生成されたフォルダ
│
├─gradle        // 自動生成されたフォルダ
│
└─src           // 作成するフォルダ
    ├─job       // 作成するフォルダ:テスト対象コード格納用
    └─tests     // 作成するフォルダ
        └─job   // 作成するフォルダ:テストコード格納用

ソース格納フォルダを作成したら、設定ファイルであるbuild.gradleを編集します。

// コメント付き設定は、gradle init2: applicationを選択したときに生成されるbuild.gradleから拝借すればよいです。
なので、筆者とはインストールしたJavaやGradleのバージョンが違うよ、という方も安心だと思います。

build.gradle
plugins {
    // Apply the groovy Plugin to add support for Groovy.
    id 'groovy'
}

repositories {
    maven { url 'https://repo.jenkins-ci.org/releases/' }
    
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

dependencies {
    testImplementation 'com.lesfurets:jenkins-pipeline-unit:1.9'

    // Use the latest Groovy version for building this library
    implementation 'org.codehaus.groovy:groovy-all:3.0.8'

    // Use the awesome Spock testing and specification framework even with Java
    testImplementation 'org.spockframework:spock-core:2.0-groovy-3.0'
    testImplementation 'junit:junit:4.13.2'
}

test {
    systemProperty "printstack.disabled", false
}

sourceSets {
    test {
        groovy {
            srcDirs= ['src']
        }
    }
}

何がハマったかというと、公式(Jenkins Pipeline Unit testing framework)の説明ではbuild.gradleの編集について、
以下2行を足してくれ、ということしか書かれていないのです。。。

  • maven { url 'https://repo.jenkins-ci.org/releases/' }
  • testImplementation 'com.lesfurets:jenkins-pipeline-unit:1.9'

Gradleを分かっている方なら、きっと余裕なんだと思いますが、Gradle初心者の筆者には厳しかったです。

test { } のsystemProperty "printstack.disabled", falseは公式の説明がありません。
しかし、これがないと、テストコードにprintCallStack()を書いても、コールスタックは出力されません。
(frameworkのコードを解析するのに、小一時間かけてしまいました・・・)

3. テスト対象コードを作成する

テストされるJenkinsfileを作成します。
筆者は、src/job/Jenkinsfileとして、公式の説明通りの内容で作成しました。

Jenkinsfile
node {
    stage('Process output') {
        if (fileExists('output') && readFile('output') == 'FAILED!!!') {
            currentBuild.result = 'FAILURE'
            error 'Build failed'
        }
    }
}

テストコードの中で、テスト対象コードのファイルパスを指定するので、ルートフォルダ以下なら、格納先フォルダはどこでも良さそうです。

4. テストコードを作成する

作成したJenkinsfileをテストするためのコードを作成します。
以下の例は、src/tests/job/JenkinsfileTest.groovyとして作成しました。

JenkinsfileTest.groovy
package tests.job

import org.junit.Before
import org.junit.Test

import com.lesfurets.jenkins.unit.BasePipelineTest

class JenkinsfileTest extends BasePipelineTest {
    @Override
    @Before
    void setUp() throws Exception {
        super.setUp()
    }
    
    @Test
    void executeTest() {
        runScript("src/job/Jenkinsfile")
        assertJobStatusSuccess()
        printCallStack()
    }

    @Test
    void exampleReadFileTest() {
        helper.addFileExistsMock('output', true)
        helper.addReadFileMock('output', 'FAILED!!!')
        runScript("src/job/Jenkinsfile")
        assertJobStatusFailure()
    }
}

公式(Jenkins Pipeline Unit testing framework)の説明に書かれている
import com.lesfurets.jenkins.unit.BasePipelineTest
だけでなく、アノテーションするためのimportも必要です。
(実際は色々まとめて、import org.junit.*とすれば良いと思います)

また、frameworkが用意してくれている各種モックを使うとき、(少なくとも自分の環境では)オーバーライドしたsetUp()super.setUp()しておかないと、使えませんでした。
公式の説明だとMocking readFile and fileExistsの章以降は、そのことに触れていないので、ハマりました。

5. テストを実行する

ルートフォルダがカレントのDOS窓で、gradle clean build testと打ちます。
(testするにはbuildが必要なので、gradle clean testでも良いはず)

C:\Work\pipelineUnitTest>gradle clean build test

BUILD SUCCESSFUL in 9s
4 actionable tasks: 4 executed
C:\Work\pipelineUnitTest>

ビルドが成功するとルートフォルダ直下にbuildフォルダが生成されているので、
build\reports\tests\test\index.htmlをブラウザで表示すれば、テスト結果を確認できます。
コールスタックも、ブラウザに表示されるStandard outputボタンを押すと確認できます。

おわりに

ソース格納フォルダの構成とか、build.gradleの書き方とか、その他諸々で、よろしくない点があれば、ご指摘ください。

公式(Jenkins Pipeline Unit testing framework)の説明について、自分がハマったことを色々と書いていますが、公式を貶めるつもりはなく、自分の知識不足なところを補完できて良かったと思っています。また、この記事で取り上げた説明は、記事を作成し始めた2021/9/11時点の内容を参照しています。そのため、この記事を投稿した後で、説明は修正されているかもしれません。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?