Gradleのtestタスクでソースコードがコピーされない

こんにちは。beaglesoftの真鍋です。

IntelliJでJavaクラスファイルを作成するところがKotlinファイルを作成してからKotlin使い始めました。

さて、先日Gradleを利用してテストを実行しよう(gradle clean test)としたところ、:test NO-SOURCEと表示されテストが実行されない事象に遭遇しました。あまりにもアタリマエのことなのでGoogleで検索しても出てこなかったため簡単にまとめてみます。

状況

プロジェクトの構成はGradleのJavaプラグインの規約に準拠したプロジェクト構成でした。

.
├── README.md
├── beagle-customer.iml
├── build
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── src
    ├── main
    │   ├── java
    │   │   └── net
    │   │       └── beaglesoft
    │   │           └── customer
    │   └── resources
    │       ├── META-INF
    │       │   └── net
    │       │       └── beaglesoft
    │       │           └── customer
    │       ├── application-prod.properties
    │       ├── application-staging.properties
    │       ├── application.properties
    │       ├── db
    │       │   └── migration
    │       ├── logback-spring.xml
    │       ├── static
    │       └── templates
    └── test
        ├── java
        │   └── net
        │       └── beaglesoft
        │           └── customer
        │               ├── controllers
        │               │   ├── BarControllerTest.groovy
        │               │   └── ...
        │               └── services
        │                   ├── FooService.groovy
        │                   └── ...
        └── resources
            ├── application.properties
            └── logback-spring.xml

また、build.gradleは特段の処理を追加することもなく以下のような標準的な内容となっていました。

group 'beagle-customer'
version '1.0-SNAPSHOT'

buildscript {
    ext.kotlin_version = '1.1.2'
    ext {
        springBootVersion = '1.5.4.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

repositories {
    mavenCentral()
    repositories {
        mavenCentral artifactUrls: [
                'http://jasperreports.sourceforge.net/maven2',
                'http://jaspersoft.artifactoryonline.com/jaspersoft/third-party-ce-artifacts/'
        ]
    }
}

apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: "groovy"
apply plugin: 'idea'
apply plugin: 'jacoco'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

idea {
    module {
        inheritOutputDirs = false
        outputDir = file("$buildDir/classes/main/")
    }
}

springBoot {
    mainClass = "net.beaglesoft.customer.CustomerApp"
}

[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'

// DOMA用の設定
processResources.destinationDir = compileJava.destinationDir
compileJava.dependsOn processResources

dependencies {
.. 省略
}

task wrapper(type: Wrapper) {
    gradleVersion = '3.4.1'
}

原因

原因はテストファイルにありました。今回テストは[Spock](http://spockframework.org/]を利用しているため、テストファイルの保存先はjavaではなくgroovyとするのが正しいということがわかりました。

.
├── README.md
├── beagle-customer.iml
├── build
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── src
    ├── main
    │   ├── java
    │   │   └── net
    │   │       └── beaglesoft
    │   │           └── customer
    │   └── resources
    │       ├── META-INF
    │       │   └── net
    │       │       └── beaglesoft
    │       │           └── customer
    │       ├── application-prod.properties
    │       ├── application-staging.properties
    │       ├── application.properties
    │       ├── db
    │       │   └── migration
    │       ├── logback-spring.xml
    │       ├── static
    │       └── templates
    └── test
        ├── groovy
        │   └── net
        │       └── beaglesoft
        │           └── customer
        │               ├── controllers
        │               │   ├── BarControllerTest.groovy
        │               │   └── ...
        │               └── services
        │                   ├── FooService.groovy
        │                   └── ...
        └── resources
            ├── application.properties
            └── logback-spring.xml

気づいてしまえば当たり前なのですが、ミニマムプロジェクトを作成して同じようにGradleの設定を行ってビルドを実行していたところ記述するテストごとにtestディレクトリにディレクトリが作成されていることに気づいたのが最初でした。

その後Groovyプラグインのドキュメントをよく読んでみるとまさにそのことが記述されていました。

[The Groovy Plugin - Gradle User Guide Version 3.3] (https://docs.gradle.org/3.3/userguide/groovy_plugin.html#sec:groovy_project_layout)

src/test/java Test Java source

src/test/groovy Test Groovy sources. May also contain Java sources for joint compilation.

結論

ドキュメントはよく読まないといけないのですが、どこを読めばいいか(すべてを読むことは最初は難しい…)を探るためにも、単純なミニマムコードを用意してうまく行かないことを確認するのは大事ですね。

あと、Gradle徹底入門 次世代ビルドツールによる自動化基盤の構築にはとてもおせわになりました。うまく行かないときはGoogleで検索して出てきたコードの断片をとりあえず貼り付けて動くか?なんてことをやりがちなのですが、正しい情報でないこともありました。

Gradleはどういうものかということがとてもわかり易く記述されていたので役立ちました。公式ガイドもわかりやすいのですが、紙の書籍のほうがパラパラと見れてよかったです。

SpringBootTestでのapplication.propertiesの設定を変更する

SpringBootTestでapplication.propertiesの値を設定する

SpringBootTestでapplication.propertiesの値を変更する方法は以下の通りとなります。

application.properties

今回対象とするapplication.propertiesの項目は以下の通りとなります。

...
foo.bar.enable=false
...

通常のテストを実行するときにはfoo.bar.enableの値はfalseとしますが、今回のテスト対象実行時のみtrueへ設定することを想定します。

SpringBootTest

SpringBootTestでテスト実行時にapplication.propertiesのfoo.ba.enableの値をtrueに設定するには、以下の通りSpringBootTestアノテーションのpropertiesfoo.bar.enable=trueのように設定します。複数の項目を指定するときにはカンマで区切ることで設定可能となります。

package ...

@Slf4j
@Transactional
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
        properties = { "foo.bar.enable=true" })
public class SampleTest {
  ...
}

これにより、SampleTest実行時にはfoo.bar.enableの値はtrueが設定されるようになります。

参考

The @SpringBootTest annotation also has a properties attribute that can be used to specify any additional properties that should be defined in the Environment. Properties are now loaded in the exact same way as Spring’s regular @TestPropertySource annotation.

Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発

Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発

Railsで単語の単数・複数変換を制御する

Railsでは単語の単数・複数をsingularize/pluralizeで変換することができますが、変換を行いたくないときもあります。そのようなときにはinflections.rbへ設定を追加することで変換を制御することができます。

Railsのバージョン

$ bundle list | grep rails 
  * rails (4.2.7)

config/initializers/inflections.rb

config/initializers/inflections.rbにはあらかじめ以下のコメントが記述されています。

# Be sure to restart your server when you modify this file.

# Add new inflection rules using the following format. Inflections
# are locale specific, and you may define rules for as many different
# locales as you wish. All of these examples are active by default:
 ActiveSupport::Inflector.inflections(:en) do |inflect|
#   inflect.plural /^(ox)$/i, '\1en'
#   inflect.singular /^(ox)en/i, '\1'
#   inflect.irregular 'person', 'people'
 #   inflect.uncountable %w( fish sheep )
 end

# These inflection rules are supported but not enabled by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
#   inflect.acronym 'RESTful'
# end

それぞれの項目は以下のとおりとなっています。

  • pluralは複数形への変換を定義
  • singularは単数形への変換を定義
  • irregularは単数形と複数形で規則性がない変換を定義
  • uncountableは孵化産名刺を定義

plural

複数形への変換処理を定義します。ここで定義することによりplularize実行時に変換規則が適用されます。

`config/initializers/inflections.rb`
 inflect.plural /^(ox)$/i, '\1en'

>> 'ox'.pluralize
"oxen"
>> 'oX'.pluralize
"oXen"

inflect.plural /^(ox)$/i, '\1en'について

inflect.plural /^(ox)$/i, '\1en'は、以下の通り記述する必要があります。

inflect.plural {単数形のパターン} {複数形の出力内容}

このとき、/^(ox)$/i, '\1en'は単数形のパターンにマッチした文字列を複数形の出力内容に定義している1で受け取って出力しています。初見では何をしているのかわからなかったのですが、正規表現のキャプチャを使用しています。

詳説 正規表現 第3版

詳説 正規表現 第3版

  • 作者: Jeffrey E.F. Friedl,株式会社ロングテール,長尾高弘
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2008/04/26
  • メディア: 大型本
  • 購入: 24人 クリック: 754回
  • この商品を含むブログ (87件) を見る

詳説 正規表現 P.132 3.4.5.1 キャプチャーしグループ化する括弧を参照

singular

単数形への変換処理を定義します。ここで定義することによりsingularize実行時に変換規則が適用されます。

`config/initializers/inflections.rb`
  inflect.singular /^(ox)en/i, '\1'

>> 'oxen'.singularize
"ox"
>> 'oXen'.singularize
"oX"

irregular

変換が不規則な処理を定義します。ここに定義することでsingularizepluralizeのそれぞれで変換が行われます。

`config/initializers/inflections.rb`
 inflect.irregular 'person', 'people'

>> 'people'.singularize
"person"
>> 'person'.pluralize
"people"

inflect.irregularについて

inflect.irregularは以下の通り定義する必要があります。

 inflect.irregular {単数形の文字列}, {複数形の文字列}

ここで定義できる引数は正規表現は指定することはできず文字列だけを定義することができます。

uncountable

uncountableには不可算名詞を定義します。ここに定義することでsingularize/pluralizeの結果が同一のものとなります。

`config/initializers/inflections.rb`
 inflect.uncountable %w( fish )

>> 'fish'.singularize
"fish"
>> 'fish'.pluralize
"fish"

おわりに

今回はenumで定義した値をuncountableに定義したかっただけだったのですが、調べていくといろいろと面白かったです。昔のプロジェクトで税をtaxとしたときにいろいろとこんがらがったことがあったことを思い出しました。

Ruby on Rails 5アプリケーションプログラミング

Ruby on Rails 5アプリケーションプログラミング