2017年8月18日金曜日

maven-surefire-pluginでテスト実行時のjavaを指定する

pomの定義

jmvパラメータにテスト実行時に使用するjavaを指定します。
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <configuration>
    <jvm>/opt/java/jdk1.6.0_45/bin/java</jvm>
  </configuration>
</plugin>

動作確認用のテストクラス

public class HogeTest {
    @Test
    public void name() throws Exception {
        System.out.println("System.getProperty(\"java.version\") = " + System.getProperty("java.version"));
    }
}

実行結果

指定したバージョンでテストが実行されていることが確認できます。
$ export JAVA_HOME=/opt/java/jdk1.8.0_141
$ mvn test

********** 省略 **********
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running HogeTest
System.getProperty("java.version") = 1.6.0_45
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.955 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

2017年8月4日金曜日

Java9でJMockit

Java9でのJMockitについて


build.gradleの定義

dependencies {
  testCompile 'org.jmockit:jmockit:1.33'
  testCompile 'junit:junit:4.12'
}

何も設定をせずにテストを実行すると

jmockitのライブラリを-javaagentに設定するか、システムプロパティにjdk.attach.allowAttachSelfを設定してください例外が発生します。
Caused by: java.lang.IllegalStateException: Running on JDK 9 requires
 -javaagent:/jmockit-1.n.jar or -Djdk.attach.allowAttachSelf

テスト実行時のシステムプロパティを追加する

Gradleのテストタスク実行時に、上の例外で指定されたシステムプロパティを設定します。
test {
  jvmArgs "-Djdk.attach.allowAttachSelf"
}

システムプロパティ追加後にテストを実行

このようにテストが正常に実行されます。
➜  java9 ./gradlew clean test

BUILD SUCCESSFUL in 2s
3 actionable tasks: 3 executed

gradle実行時にワーニングログが出力される場合

Gradle実行時にこのようなワーニングが出力される場合があります。
その場合は、Gradleのバージョンを4.1以上(現時点では4.1-rc-2が最新)にします。
➜  java9 ./gradlew clean test
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.gradle.internal.reflect.JavaMethod
WARNING: Please consider reporting this to the maintainers of org.gradle.internal.reflect.JavaMethod
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

2017年7月29日土曜日

SQL Maven Pluginを使ってデータベースのセットアップ

SQL Maven Pluginを使うことで、Mavenからデータベースのセットアップを行うことができます。

pomにpluginの定義を行う

sql-maven-pluginの追加と、接続したいデータベースに対応したJDBCドライバをdependenciesに追加します。
configurationには、接続先データベースの情報と実行したいSQLファイルのパスを設定します。

<build>
  <plugins>
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>sql-maven-plugin</artifactId>
      <version>1.5</version>
      <dependencies>
        <!-- specify the dependent jdbc driver here -->
        <dependency>
          <groupId>org.postgresql</groupId>
          <artifactId>postgresql</artifactId>
          <version>42.1.3</version>
        </dependency>
      </dependencies>

      <configuration>
        <driver>org.postgresql.Driver</driver>
        <url>jdbc:postgresql://localhost:5432/test_db</url>
        <username>test</username>
        <password>test</password>
        <srcFiles>
          <srcFile>src/test/resources/test.sql</srcFile>
        </srcFiles>
      </configuration>
    </plugin>
  </plugins>
</build>


実行結果

実行するとSQLフィルが実行されたことなどが標準出力に出力されます。

$ mvn sql:execute                                  
[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building sql-maven-plugin 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- sql-maven-plugin:1.5:execute (default-cli) @ sql-maven-plugin ---
[INFO] Executing file: /tmp/test.838893334sql
[INFO] 1 of 1 SQL statements executed successfully
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.428 s
[INFO] Finished at: 2017-07-29T06:59:17+09:00
[INFO] Final Memory: 9M/238M
[INFO] ------------------------------------------------------------------------

2017年7月15日土曜日

Tomcat EmbeddedでAccessLogValveを適用してアクセスログを出力する

Tomcat EmbeddedでTomcatのアクセスログを出力する方法

META-INF/context.xmlを作成してAccessLogValveを適用する

context.xmlを用いて、下のようにAccessLogValveを適用する
<?xml version="1.0" encoding="UTF-8"?>
<Context>
  <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
      prefix="access" suffix=".log" pattern="%h %l %u %t &quot;%r&quot; %s %b" />

</Context>

Tomcat Embeddedを使ってTomcatを起動する

final Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);

final String docBase = new File("src/main/webapp").getAbsolutePath();
final StandardContext context = (StandardContext) tomcat.addWebapp("/", docBase);

final File additionWebInfClasses = new File("build/classes/main");
final StandardRoot resources = new StandardRoot(context);
resources.addPreResources(
        new DirResourceSet(resources, "/WEB-INF/classes",
                additionWebInfClasses.getAbsolutePath(), "/"));
context.setResources(resources);

tomcat.start();
tomcat.getServer().await();

結果

カレントディレクトリ配下のtomcat.8080/logs配下にアクセスログが出力されるようになる。

➜  tomcat-embedded-example cat tomcat.8080/logs/access.2017-07-15.log 
127.0.0.1 - - [15/Jul/2017:15:36:39 +0900] "GET / HTTP/1.1" 200 89
127.0.0.1 - - [15/Jul/2017:15:36:43 +0900] "GET /hoge HTTP/1.1" 404 1002

2017年6月16日金曜日

[Tomcat]StuckThreadDetectionValveを使って遅延した処理の情報をログに出力する

StuckThreadDetectionValveを使うことで、処理遅延が発生したスレッドを検知することができる。

使い方は下の通り

設定を追加する

conf/server.xmlにStuckThreadDetectionValveを使用するように設定をする。

thresholdを使って、どれぐらい遅延したら検知するかを設定する。
デフォルトは600秒となっている。下のように60と設定すると60秒となる。

  


ログの結果

単純に一定時間スリープするServletで試すと下のようなログが出力される。

16-Jun-2017 21:14:37.445 警告 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.valves.StuckThreadDetectionValve.notifyStuckThreadDetected Thread [http-nio-8080-exec-6] (id=[29]) has been active for [62,731] milliseconds (since [17/06/16 21:13]) to serve the same request for [http://localhost:8080/hoge] and may be stuck (configured threshold for this StuckThreadDetectionValve is [60] seconds). There is/are [1] thread(s) in total that are monitored by this Valve and may be stuck.
java.lang.Throwable
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at hoge.HogeServlet.doGet(HogeServlet.kt:11)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)

その後処理が完了すると、下のようなログも出力される

16-Jun-2017 21:15:37.465 警告 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.valves.StuckThreadDetectionValve.notifyStuckThreadCompleted Thread [http-nio-8080-exec-6] (id=[29]) was previously reported to be stuck but has completed. It was active for approximately [120,031] milliseconds.

これで、処理遅延などが発生した場合に自動的にスレッドダンプが取得できる。
障害時にスレッドダンプとってません問題も回避できそうな気もする。

2017年5月31日水曜日

AssertJ-DBを使ってデータベースの値をアサート

AssertJ-DBを使ってデータベースのテーブルの値などをアサートする方法

assertj-dbを依存に追加する

testCompile 'org.assertj:assertj-db:1.2.0'

テーブルの内容

ID,NAME
1,name
2,name_2



シンプルにテーブルの内容をアサートする例

Tableクラスを使うことでテーブルの内容全体に対してアサートを行なうことができます。

final OracleDataSource source = new OracleDataSource();
// 接続先情報は省略
final Table.Order order = Table.Order.asc("id");
final Table table = new Table(source, "test_table", new Table.Order[] {order});

assertThat(table)
        .column("id")
        .value().isEqualTo(1L)
        .value().isEqualTo(2L)
        .column("name")
        .value().isEqualTo("name")
        .value().isEqualTo("name_2");

SQLの結果に対してアサート

Requestクラスを使ってSQLを実行し、その取得内容をアサートすることができます。
final Request request = new Request(source, "select name from test_table where id = 2");

assertThat(request).column(0).value().isEqualTo("name_2");

テーブルの変更内容をアサートする

Changesクラスを使うことで、開始ポイントと終了ポイントを定義しその間の変更内容をアサートすることができます。
この例では、1レコードの変更が正しいことをアサートしています。
final Table table = new Table(source, "test_table");
final Changes changes = new Changes(table);
changes.setStartPointNow();

try (final Connection connection = source.getConnection();
     final PreparedStatement statement = connection.prepareStatement(
             "update test_table set name = ? where id = 2")) {
    statement.setString(1, "へんこうご");
    statement.execute();
}

changes.setEndPointNow();
assertThat(changes)
        .hasNumberOfChanges(1)                       // 1レコード変更されている
        .ofModification().hasNumberOfChanges(1)      // 変更内容は更新
        .change()
        .columnAmongTheModifiedOnes()
        .hasColumnName("name")
        .hasValues("name_2", "へんこうご");          // name_2からへんこうごに変更されていること

2017年4月13日木曜日

文字列からLocalDateを作る方法

DateTimeFormatterを使って文字列をパースする。

final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
final LocalDate localDate = LocalDate.parse("2017/04/13", formatter);

2017年1月11日水曜日

[JMockit]特定のテストでだけモックを定義する

JMockitで特定のテストでモックを使う方法

使用バージョン

    
      org.jmockit
      jmockit
      1.30
      test
    

サンプルコード

この例では、initialCapacityを受け取るコンストラクタとaddメソッドにモック実装を定義しています。
new MockUp<ArrayList<String>>() {
    @Mock void $init(int initialCapacity) {
        System.out.println("initialCapacity = " + initialCapacity);
    }
    
    @Mock boolean add(String s) {
        System.out.println("s = " + s);
        return true;
    }
};
final List<String> strings = new ArrayList<>(10);
strings.add("hoge");

実行結果

initialCapacity = 10
s = hoge