batファイルをMakefileのようにサブルーチン指定で実行する
概要
自分の開発環境はMac
なのでmake
が初期状態から実行できるのですが、
チーム開発を行うときにWindows
な人がいることもありますよね。
MacでもWindowsでも環境構築をワンライナーで終わらせたいと思ったときに、 Makefileと同じような書き方をしつつ、コマンドプロンプトでどうにかできないのか調べたメモです。
環境
TL;DR
- batファイルにはサブルーチンという関数に近い機能がある
- サブルーチンは
:ラベル名
で作る - ファイルの先頭でサブルーチンを呼ぶために
goto
を置く call makefile.bat <label-name>
どうすればいいか
batファイルの作成
まずは普通のbatファイルを作ります。
その時、先頭にgoto
を置きます。
@echo off goto %1
サブルーチンの設定
コマンドプロンプトから実行したい内容をサブルーチンにします。
サブルーチンの最後はexit /b
で呼び出し元に戻ります。
@echo off goto %1 :sub echo "これはサブルーチン" exit /b :not-called echo "これは呼ばれない" exit /b
コマンドプロンプトからの呼び出し
コマンドプロンプトからラベルを指定して実行します。
call makefile.bat sub
"これはサブルーチン"
気をつけること
ここで作ったbatファイルはそのままでは実行できなくなります。
参考
ひとつのbuild.gradle.ktsファイルでマルチプロジェクトを構成したい
概要
複数ファイルで構成するサンプルはよく見かけるけど、1つのファイルで構成するサンプルを見かけなかったので作ってみました。
これが正解かはわかりません。
環境
------------------------------------------------------------ Gradle 6.6.1 ------------------------------------------------------------ Build time: 2020-08-25 16:29:12 UTC Revision: f2d1fb54a951d8b11d25748e4711bec8d128d7e3 Kotlin: 1.3.72 Groovy: 2.5.12 Ant: Apache Ant(TM) version 1.10.8 compiled on May 10 2020 JVM: 1.8.0_242 (AdoptOpenJDK 25.242-b08) OS: Mac OS X 10.15.6 x86_64
TL;DR
- 全体の設定は
allProjects
に - サブプロジェクト全体の設定は
subprojects
に - 各プロジェクト固有の設定は
project("MODULE_NAME")
に
どうすればいいか
とりあえず親プロジェクトを用意する
ゼロから用意するのは面倒なので、SpringInitializerで生成しちゃいます。
今回は親プロジェクトはナシで、サブプロジェクトになるものを2つ用意しました。
スタートはこんな感じです。ここから少しずつ分解していきます。
rootProject.name = "one-file-gradle-multi-project" include("sub-project-1") include("sub-project-2")
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("org.springframework.boot") version "2.3.3.RELEASE" id("io.spring.dependency-management") version "1.0.10.RELEASE" kotlin("jvm") version "1.3.72" kotlin("plugin.spring") version "1.3.72" } group = "com.example.footaku.scratches" version = "0.0.1-SNAPSHOT" java.sourceCompatibility = JavaVersion.VERSION_1_8 repositories { mavenCentral() } dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.3") runtimeOnly("org.postgresql:postgresql") testImplementation("org.springframework.boot:spring-boot-starter-test") { exclude(group = "org.junit.vintage", module = "junit-vintage-engine") } } tasks.withType<Test> { useJUnitPlatform() } tasks.withType<KotlinCompile> { kotlinOptions { freeCompilerArgs = listOf("-Xjsr305=strict") jvmTarget = "1.8" } }
全体に適用したいものを抜き出す
repositoriesは全体に関係するのでallProjects
に移動します。
allprojects { repositories { mavenCentral() } }
サブプロジェクト全体に適用するものを分ける
すべてのサブプロジェクトで使うdependencies
とtasks
はsubprojects
へ移動します。
ついでにplugin
やsourceSets
の設定も足します。
subprojects { apply(plugin = "java") apply(plugin = "kotlin") apply(plugin = "kotlin-spring") apply(plugin = "org.springframework.boot") apply(plugin = "io.spring.dependency-management") group = "com.example.footaku.scratches" version = "0.0.1-SNAPSHOT" java.sourceCompatibility = JavaVersion.VERSION_1_8 java.targetCompatibility = JavaVersion.VERSION_1_8 sourceSets { main { java.srcDir("src/main/kotlin") resources.srcDir("src/main/resources") } test { java.srcDir("src/test/kotlin") resources.srcDir("src/test/resources") } } dependencies{...} tasks.withType<...> {...} }
プロジェクト固有の設定を分ける
さらに、一部のプロジェクトだけに適用するdependencies
などはproject
でプロジェクト名を指定します。
今回はDBを使う設定をsub-project-1
にだけ適用してみます。
この場合sub-project-1
はDataSourceの設定をapplication.yml(.properties)
に書かないとSpringBootは起動できなくなりますが、sub-project-2
ではその依存が入っていないので設定が無くても大丈夫です。
project(":sub-project-1") { dependencies { implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.3") runtimeOnly("org.postgresql:postgresql") } }
完成
出来上がりがこちらです。
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("org.springframework.boot") version "2.3.3.RELEASE" id("io.spring.dependency-management") version "1.0.10.RELEASE" kotlin("jvm") version "1.3.72" kotlin("plugin.spring") version "1.3.72" } allprojects { repositories { mavenCentral() } } subprojects { apply(plugin = "java") apply(plugin = "kotlin") apply(plugin = "kotlin-spring") apply(plugin = "org.springframework.boot") apply(plugin = "io.spring.dependency-management") group = "com.example.footaku.scratches" version = "0.0.1-SNAPSHOT" java.sourceCompatibility = JavaVersion.VERSION_1_8 java.targetCompatibility = JavaVersion.VERSION_1_8 sourceSets { main { java.srcDir("src/main/kotlin") resources.srcDir("src/main/resources") } test { java.srcDir("src/test/kotlin") resources.srcDir("src/test/resources") } } dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") testImplementation("org.springframework.boot:spring-boot-starter-test") { exclude(group = "org.junit.vintage", module = "junit-vintage-engine") } } tasks.withType<Test> { useJUnitPlatform() } tasks.withType<KotlinCompile> { kotlinOptions { freeCompilerArgs = listOf("-Xjsr305=strict") jvmTarget = "1.8" } } } project(":sub-project-1") { dependencies { implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.3") runtimeOnly("org.postgresql:postgresql") } }
困っていること
バージョンの指定方法
Mavenではバージョンなどの値を<properties>
で設定すればどこでも使えるけれど、
Gradleにはそういう機能がないっぽいのが困りどころだなーと感じてます。
ワークアラウンド?としてext
を使う方法をよく見かけますが、こちらはplugin
の指定では使えず、
一方でbuildSrc
に定義すればどこでも使える様になる代わりに、定義する場所が分散してしまうのでうーん…
できれば「このファイルだけ見ればOK」という状態にしたいのですが、
自分なりに「これがベスト!」という方法が見つかってません。
あってるかがわからない
とりあえず動いてるけどこれが正しいのかがわかってません。
参考
FlywayのmigrateでcleanOnValidationErrorを試す
概要
flyway
のコマンドmigrate
にあるcleanOnValidationError
というオプションを使うとどうなるかのメモです。
今回はPostgresで試してます。
環境
どうやる?
今回はmavenを使って実行したいのでpom.xmlを準備します。
... <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.2.5</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.flywaydb</groupId> <artifactId>flyway-maven-plugin</artifactId> <version>5.2.1</version> </plugin> </plugins> </build> ...
migration用のSQLを準備します。 今回は2つのテーブルを作ってみます。
--V1__create_a.sql CREATE TABLE AAA ( id NUMERIC PRIMARY KEY );
--V2__create_b.sql CREATE TABLE BBB ( id NUMERIC PRIMARY KEY );
postgresはdockerで作ります。
docker run -d \ --name postgres10 \ -e POSTGRES_USER=postgres \ -e POSTGRES_PASSWORD=postgres \ -p 5432:5432 \ postgres:10
どうなる?
1回目
mvnからflywayのmigrateコマンドを実行します。
./mvnw flyway:migrate \ -Dflyway.url=jdbc:postgresql://127.0.0.1:5432/postgres \ -Dflyway.user=postgres \ -Dflyway.password=postgres \ -Dflyway.driver=org.postgresql.Driver \ -Dflyway.locations=classpath:db/migration
[INFO] Scanning for projects... [INFO] [INFO] ------------------< com.example.panage:sample-flyway >------------------ [INFO] Building sample-flyway 0.0.1-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- flyway-maven-plugin:5.2.3:migrate (default-cli) @ sample-flyway --- [INFO] Flyway Community Edition 5.2.3 by Boxfuse [INFO] Database: jdbc:postgresql://127.0.0.1:5432/postgres (PostgreSQL 10.4) [INFO] Successfully validated 2 migrations (execution time 00:00.026s) [INFO] Current version of schema "public": << Empty Schema >> [INFO] Migrating schema "public" to version 1 - create a [INFO] Migrating schema "public" to version 2 - create b [INFO] Successfully applied 2 migrations to schema "public" (execution time 00:00.147s) [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.394 s [INFO] Finished at: 2018-12-01T12:27:09+09:00 [INFO] ------------------------------------------------------------------------
migrateが成功して、2つのSQLが実行されたことがわかります。
SQLを編集
ここでmigrateしたSQLの1つを編集してしまいます。
--V1__create_a.sql CREATE TABLE AAA ( id NUMERIC PRIMARY KEY, value VARCHAR(100) NOT NULL );
2回目
今度はcleanOnValidationError
のオプションを付けて実行します。
./mvnw flyway:migrate \ -Dflyway.url=jdbc:postgresql://127.0.0.1:5432/postgres \ -Dflyway.user=postgres \ -Dflyway.password=postgres \ -Dflyway.driver=org.postgresql.Driver \ -Dflyway.locations=classpath:db/migration \ -Dflyway.cleanOnValidationError=true ←ココ
[INFO] Scanning for projects... [INFO] [INFO] ------------------< com.example.panage:sample-flyway >------------------ [INFO] Building sample-flyway 0.0.1-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- flyway-maven-plugin:5.2.3:migrate (default-cli) @ sample-flyway --- [INFO] Flyway Community Edition 5.2.3 by Boxfuse [INFO] Database: jdbc:postgresql://127.0.0.1:5432/postgres (PostgreSQL 10.4) [INFO] Successfully cleaned schema "public" (execution time 00:00.044s) [INFO] Creating Schema History table: "public"."flyway_schema_history" [INFO] Current version of schema "public": << Empty Schema >> [INFO] Migrating schema "public" to version 1 - create a [INFO] Migrating schema "public" to version 2 - create b [INFO] Successfully applied 2 migrations to schema "public" (execution time 00:00.175s) [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.858 s [INFO] Finished at: 2018-12-01T12:29:06+09:00 [INFO] ------------------------------------------------------------------------
成功しました。
Successfully cleaned schema "public"
と出て、schemaが一度cleanされていることがわかります。
注意
このオプションをつけると問答無用でschemaがキレイになってしまうので、すでにレコードが登録されているときは、そのレコードが消えていいのか確認してから実行しましょう。
また、公式にもWarning ! Do not enable in production !
とあるように、限られた状況以外で本番の環境で使うのは避けるのが良さそうです。
参考
Command-line: migrate - Command-line: migrate - Flyway by Boxfuse • Database Migrations Made Easy.
Kotlinでtestcontainers-javaをつかう
概要
Testcontainersってなに?
プログラムからDockerコンテナを起動できるライブラリ 最近testcontainers-javaとJUnit5のjupiterへの親和性が高くなったらしいので試したメモです。
環境
どうする
pomに依存を足す
依存にはTestcontainersとJUnit5と、ドキュメントにはでてきていないような気がするけど、 Testontainersが作っているJUnit5用のExtensionが必要です 今回はPostgreSQLのコンテナを試してみるのでPostgreSQL用の依存も追加します。
<!-- pom抜粋 --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>${junit.jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers</artifactId> <version>${testcontainers.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>junit-jupiter</artifactId> <version>${testcontainers.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>postgresql</artifactId> <version>${testcontainers.version}</version> <scope>test</scope> </dependency>
テストクラスを作る
クラスに@Testcontainers
アノテーションをつけて、PostgreSQLContainer
というクラスのインスタンスを作ってあげるだけでOK。
クラス変数として作成するとテストクラスのインスタンス生成時にDBが起動して、テストが全て終わったら停止します。
インスタンス変数にすると各テストメソッドごとに起動と停止が実行されます。
インスタンス生成時にデータベース名やUsername/Passwordも一緒に設定できます。
@Suppress("NonAsciiCharacters", "TestFunctionName") @Testcontainers class SampleTestContainer { companion object { @Container private val container = PostgreSQLCotainer() .withDatabaseName("postgres") .withUsername("postgres") .withPassword("") } @Test fun コンテナが起動している() { assertTrue(container.isRunning) } }
と思ったら
SELF-TypeReferenceが使われてて、今のKotlinだとそのままインスタンス化はできない?らしいので、 継承したクラスを作ってお茶を濁すことに。。(この辺あまり詳しくないです
class PostgreSQLKotainer : PostgreSQLContainer<PostgreSQLKotainer>() // ... 抜粋 ... @Container private val container = PostgreSQLKotainer() // ...........
気をつけること
Dockerなのでどうしても起動に待ち時間が必要になります。
1回だけ実行するならそんなに気にならないけど、開発時に何回も実行するとなるとツライ。
テストを実行する前に起動して、テスト実行中はTRUNCATE
やDROP
を使いつつ、すべてのテストが終わったら停止するような使い方が良さそうに感じました。
Bashでは'readonly'より'local -r'を使っていきたい
概要
Bashでスクリプトを書いている時に、関数内に閉じた形で読み取り専用の変数を宣言できないか調べたメモです。
環境
$ bash --version GNU bash, バージョン 4.4.23(1)-release (x86_64-apple-darwin17.5.0)
readonlyはグローバル
変数を読み取り専用として宣言できるreadonly
ですが 宣言した変数自体はグローバル変数です。
#!/bin/bash set -eu function setHoge() { readonly hoge="This is hoge" } setHoge printf "hoge = ${hoge}\n" exit 0
$ ./readonly.bash
hoge = This is hoge
変数hoge
は関数の中で宣言していますが、関数の外からも参照できています。
localはスコープ内に閉じられる
local
を使えば変数を関数などのスコープ内に閉じられます。
#!/bin/bash set -eu function setHoge() { local hoge="This is hoge" printf "hoge = ${hoge}\n" hoge="This is hogehoge" printf "hoge = ${hoge}\n" } setHoge printf "hoge = ${hoge}\n" exit 0
$ ./local.bash hoge = This is hoge hoge = This is hogehoge ./local.bash: line 13: hoge: unbound variable
ただし、そのままでは書き換え可能です。
rオプションをつければ安心
local
で変数を宣言する際に-r
をつけると読み取り専用にできます。
#!/bin/bash set -eu function setHoge() { local -r hoge="This is hoge" printf "hoge = ${hoge}\n" hoge="This is hogehoge" printf "hoge = ${hoge}\n" } setHoge printf "hoge = ${hoge}\n" exit 0
$ ./local-r.bash hoge = This is hoge ./local-r.bash: line 8: hoge: readonly variable
これで安心です。
参考
DbSetupでPostgreSQLの範囲型とJSONデータ型を扱う
概要
テストでよく使うDbSetupで、PostgreSQLの範囲型とJSONデータ型をどう扱うのか試してみました。
環境
JSONデータ型
テーブルを準備
JSONデータ型をもったテーブルを作ります。
JSONデータ型にはjson
とjsonb
の2種類ありますが、今回は検索に有利なjsonb
を使います。
CREATE TABLE jsonb ( id INT PRIMARY KEY, value JSONB NOT NULL );
DbSetupから文字列としてINSERT
普通の文字列としてINSERTしてみます
val destination = DriverManagerDestination(url, username, password) val insert = Operations.insertInto("json") .columns("id", "value") .values(1L, """{ "hoge": "fuga" }""") .build() DbSetup(destination, insert).launch()
ダメでした。残念ながら型が違うということでエラーになります。
com.ninja_squad.dbsetup.DbSetupRuntimeException: org.postgresql.util.PSQLException: ERROR: column "value" is of type jsonb but expression is of type character varying ヒント: You will need to rewrite or cast the expression.
SQLをそのまま実行する
DbSetupにはJSONデータ型への変換が用意されていないようなので、素のSQLを実行するように変更します。
val insert = Operations.sql(""" INSERT INTO jsonb ( id, value ) VALUES ( ${1L}, '{ "hoge": "fuga" }') """) DbSetup(destination, insert).launch()
これはうまくいきました。
範囲型
テーブルを準備
範囲型にも何種類かありますが、daterange
を使います
CREATE TABLE range ( id INT PRIMARY KEY, value daterange NOT NULL );
DbSetupから文字列としてINSERT
こちらも普通の文字列としてINSERTしてみます。
val insert = Operations.insertInto("range") .columns("id", "value") .values(1L, "[2018-01-01, 2018-01-31]") .build() DbSetup(destination, insert).launch()
やはりダメでした。
com.ninja_squad.dbsetup.DbSetupRuntimeException: org.postgresql.util.PSQLException: ERROR: column "value" is of type daterange but expression is of type character varying ヒント: You will need to rewrite or cast the expression.
SQLをそのまま実行する
JSONの時と同じように素のSQLでINSERTしてみます。
val insert = Operations.sql(""" INSERT INTO range ( id, value ) VALUES ( ${1L}, '[2018-01-01, 2018-01-31]') """) DbSetup(destination, insert).launch()
こちらもうまくいきました。
テストで便利なDbSetupですが、データベース固有の方言をサポートしていないところもあるようです。
そんな時は純粋にSQLを実行するのが良いようです。
PostgreSQLで日付の範囲型を使う
PostgreSQLの範囲型
PostgreSQLには範囲型という型が用意されています。
数値や日付の範囲型もありますが、今回は日時の範囲型であるtsrange
を使ってみます。
TABLEの作成
いつもと同じように、テーブルを作成するときの型にtsrange
を指定するだけです。
postgres=# CREATE TABLE hoge ( postgres(# id INT PRIMARY KEY, postgres(# duration TSRANGE NOT NULL postgres(# ); CREATE TABLE
INSERTはどうするか
こちらもいつもと同じようにINSERTを実行すれば大丈夫です。
postgres=# INSERT INTO hoge (id, duration) postgres-# VALUES (1, '[2018-01-01, 2018-02-01)'); INSERT 0 1
SELECTはどうするか
ためしにそのままSELECTを試します。
postgres=# SELECT * FROM hoge; id | duration ----+----------------------------------------------- 1 | ["2018-01-01 00:00:00","2018-02-01 00:00:00")
大丈夫そうです。
下限や上限だけ取りたい
組み込み関数を使うと上限や下限が取得できます
postgres=# SELECT LOWER(duration) FROM hoge; lower --------------------- 2018-01-01 00:00:00 (1 row) postgres=# SELECT UPPER(duration) FROM hoge; upper --------------------- 2018-02-01 00:00:00 (1 row)
任意の日時が範囲内か知りたい
範囲型に対して@>
を使って範囲内か確認できます
postgres=# SELECT * FROM hoge WHERE duration @> '2018-01-01 0:0:0' :: TIMESTAMP; id | duration ----+----------------------------------------------- 1 | ["2018-01-01 00:00:00","2018-02-01 00:00:00") (1 row) postgres=# SELECT * FROM hoge WHERE duration @> '2018-02-01 0:0:0' :: TIMESTAMP; id | duration ----+---------- (0 rows)
検索条件が範囲内のときのみレコードを取得できました。
今回はすべての機能は試していませんが、他にもたくさん関数や演算子が用意されているので便利に使えそうです。
KotlinとJUnit5とTestInstanceアノテーション
概要
JavaでJUnit5を使っているときに、KotlinだとBeforeAll
とAfterAll
はどうなるのだろうと試したメモです。
環境
- Kotlin 1.2.60
- JUnit 5.2.0(jupiter)
試したこと
インスタンスメソッドでいい?
インスタンスメソッドにBeforeAll
やAfterAll
アノテーションをつけると怒られます。
(IDEA使ってるとメソッド名がグレーアウトになるので、事前に実行されなさそうなこともわかります)
package com.example.panage.junit5 import org.junit.jupiter.api.* class TestInstancePerMethodSample { @BeforeAll fun execBeforeAllTest() { println("before all") } @BeforeEach fun execBeforeEachTest() { println("before each") } @Test fun hoge() { println("feature") } @AfterEach fun execAfterEachTest() { println("after each") } @AfterAll fun execAfterAllTest() { println("after all") } }
org.junit.platform.commons.JUnitException: @BeforeAll method 'public final void com.example.panage.junit5.TestInstanceSample.execBeforeAllTest()' must be static unless the test class is annotated with @TestInstance(Lifecycle.PER_CLASS). at org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.assertStatic(LifecycleMethodUtils.java:59) at org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.lambda$findMethodsAndAssertStatic$0(LifecycleMethodUtils.java:83) at java.util.ArrayList.forEach(ArrayList.java:1257) at java.util.Collections$UnmodifiableCollection.forEach(Collections.java:1080)
staticメソッドにするか、TestInstanceアノテーション(後述)を使うように言われます。
companion objectだけでいい?
companion object
の中に入れてみます。
companion object { @BeforeAll fun execBeforeAllTest() { println("before all") } @AfterAll fun execAfterAllTest() { println("after all") } }
before each feature after each Process finished with exit code 0
テストは実行できますが、BeforeAll
とAfterAll
の2つは実行されません。
JvmStaticアノテーションをつけたら?
@JvmStatic
を追加してみます。
companion object { @BeforeAll @JvmStatic fun execBeforeAllTest() { println("before all") } @AfterAll @JvmStatic fun execAfterAllTest() { println("after all") } }
before all before each feature after each after all Process finished with exit code 0
ちゃんとBeforeAll
とAfterAll
のメソッドも実行されました。
TestInstanceアノテーションなら?
先程のエラーメッセージでTestInstance
アノテーションを使うように言われていたのでつけてみます。
クラスに@TestInstance(TestInstance.Lifecycle.PER_CLASS)
を足して、BeforeAll
とAfterAll
をcompanion object
の外に出します。
package com.example.panage.junit5 import org.junit.jupiter.api.* @TestInstance(TestInstance.Lifecycle.PER_CLASS) class TestInstancePerClassSample { @BeforeAll fun execBeforeAllTest() { println("before all") } @AfterAll fun execAfterAllTest() { println("after all") } @BeforeEach fun execBeforeEachTest() { println("before each") } @Test fun testFeature() { println("feature") } @AfterEach fun execAfterEachTest() { println("after each") } }
before all before each feature after each after all Process finished with exit code 0
ちゃんと実行できました!
TestInstanceアノテーション?
テストクラスのライフサイクルを変更するためのアノテーションです(そのまんま)
PER_CLASS
とPER_METHOD
が用意されていて、何も設定していない状態だとPER_METHOD
になります。
PER_METHOD
は従来どおりの動作で、テストメソッド実行ごとにテストクラスのインスタンスが生成されます。
PER_CLASS
では各テストメソッドに、同じインスタンスが使われます。(@Nested
を併せて使うと少し話は変わってくるようです)
設定の変更はアノテーションの他に、MavenやSystemプロパティなどからデフォルトでどちらにするかを変えられます。
junit-platform.properties
というファイルに書いてクラスパスルートに置くのが個人的には良いかな?と思っています。
TestInstanceとKotlinの関係はAPIドキュメントにも
Simplified declaration of @BeforeAll and @AfterAll methods in test classes implemented with the Kotlin programming language.
とあります。
デフォルトの設定を変えるのか、クラスごとに設定するのかはチームごとの方針に依るとは思いますが、
KotlinでBeforeAll
とAfterAll
を使うときは、PER_CLASS
にしておくと楽になりそうです。
参考
IntelliJ IDEA上でmvn-testをDebugするときはmaven-surefire-pluginが必要らしい
概要
mvn test
中にBreakPointでとまらないなーと思って調べたメモです。
環境
- IntelliJ IDEA 2018.2.2 (Ultimate Edition)
どうすればいいか
タイトルにも書いてありますが、maven-surefire-plugin
を使います。
build pluginとして設定して、mvn実行時のオプションに-DforkCount=0
を追加します。
pom.xml
<build> ... <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.0</version> </plugin> </plugins> </build>
起動構成
起動構成に入れるのを忘れがちな場合はConfigurationに追加してしまってもいいと思います。
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.0</version> <configuration> <forkCount>0</forkCount> </configuration> </plugin>
参考
PostgreSQLでデフォルトの権限を設定する
コマンドはALTER DEFAULT PRIVILEGES
標準SQLにはないコマンドです。
USERに設定してみる
今回はDockerコンテナで環境を準備しました。
docker run -d \ --name postgres10\ -e POSTGRES_USER=postgres\ -e POSTGRES_PASSWORD=postgres\ postgres:10.4
コンテナ内からpsqlで接続します。
$ docker exec -ti postgres10 /bin/bash $ su - postgres $ psql -U postgres
新しいROLEを作成
postgres=# select session_user; session_user -------------- postgres postgres=# CREATE ROLE testrole WITH NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT LOGIN;CREATE ROLE postgres=# CREATE TABLE test1 (id int); CREATE TABLE postgres=# \z Access privileges Schema | Name | Type | Access privileges | Column privileges | Policies --------+-------+-------+-------------------+-------------------+---------- public | test1 | table | | |
SELECTできるか確認します。
$ psql -U testrole -d postgres
postgres=> select session_user; session_user -------------- testrole postgres=> select * from test1; ERROR: permission denied for relation test1
権限が無いのでできません。
新しい権限を設定
今度はDEFAULT権限を変更したあとでTABLEを作ります。
postgres=# select session_user; session_user -------------- postgres postgres=# ALTER DEFAULT PRIVILEGES GRANT SELECT ON TABLES TO testrole; ALTER DEFAULT PRIVILEGES postgres=# CREATE TABLE test2 (id int); CREATE TABLE postgres=# \z Access privileges Schema | Name | Type | Access privileges | Column privileges | Policies --------+-------+-------+---------------------------+-------------------+---------- public | test1 | table | | | public | test2 | table | postgres=arwdDxt/postgres+| | | | | testrole=r/postgres | | (2 rows)
postgres=> select session_user; session_user -------------- testrole postgres=> select * from test2; id ---- (0 rows) postgres=> select * from test1; ERROR: permission denied for relation test1
権限を設定したあとに作ったtest2
テーブルは参照できました。
一方で、先に作ったtest1
は参照できないままです。
設定した権限を削除
設定を削除してみます。
postgres=# select session_user; session_user -------------- postgres postgres=# ALTER DEFAULT PRIVILEGES REVOKE SELECT ON TABLES FROM testrole; ALTER DEFAULT PRIVILEGES postgres=# CREATE TABLE test3 (id int); CREATE TABLE postgres=# \z Access privileges Schema | Name | Type | Access privileges | Column privileges | Policies --------+-------+-------+---------------------------+-------------------+---------- public | test1 | table | | | public | test2 | table | postgres=arwdDxt/postgres+| | | | | testrole=r/postgres | | public | test3 | table | | | (3 rows)
postgres=> select session_user; session_user -------------- testrole postgres=> select * from test3; ERROR: permission denied for relation test3 postgres=> select * from test2; id ---- (0 rows)
設定を削除したあとのtest3
テーブルは参照できませんが、
その前に作っていたtest2
テーブルへの参照権限は残されたままです。
公式ドキュメントにあるとおり、すでに設定されている権限には影響しないので、DB構築初期に実施するのがよさそうです。