はじめに
業務で、JenkinsのPipelineスクリプト(いわゆるJenkinsfile
というファイル名の、Groovyスクリプト)を書くことが度々あります。その単体テストができるということで
Jenkins Pipeline Unit testing frameworkというものを使ってみようとしたのですが。。。
筆者の知識が十分でなかったために、休日を1日溶かしました。
- Javaでの開発経験ならあるが、機能追加・不具合対応ばかりなので、既存のコードについて完璧には知らない
- 今回は、JUnitのアノテーション(C#でいう属性のような、関数定義の冒頭につけるアレ)を思い出せなかった
そこで、同じような目に会う方が減ってくれるといいな、という思いで投稿します。
今更Jenkins、という方もいるでしょう。しかし、組み込みソフト開発の業界は他のソフト開発業界での流行が、数年遅れて来ると思っているので、少しはニーズがあると嬉しいです。
構築手順
ハマったことも添えながら、手順を示していきます。
事前準備のところは、ネットに転がっているので、省略します。
(DOS窓で、java --version
とかgradle -v
とか打って、返事もらえていればOK)
-
事前準備
- JDKのインストール
- Gradleのインストール
-
手順概要
- ルートフォルダを作成して、
gradle init
する - ソース格納フォルダを作成して、
build.gradle
を編集する - テスト対象コードを作成する
- テストコードを作成する
- テストを実行する
- ルートフォルダを作成して、
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 init
で2: application
を選択したときに生成されるbuild.gradle
から拝借すればよいです。
なので、筆者とはインストールしたJavaや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
として、公式の説明通りの内容で作成しました。
node {
stage('Process output') {
if (fileExists('output') && readFile('output') == 'FAILED!!!') {
currentBuild.result = 'FAILURE'
error 'Build failed'
}
}
}
テストコードの中で、テスト対象コードのファイルパスを指定するので、ルートフォルダ以下なら、格納先フォルダはどこでも良さそうです。
4. テストコードを作成する
作成したJenkinsfile
をテストするためのコードを作成します。
以下の例は、src/tests/job/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時点の内容を参照しています。そのため、この記事を投稿した後で、説明は修正されているかもしれません。