BullよりElk

洋服と食をこよなく愛するWebプログラマ。

JJUG CCC 2017 FallでAssertJを推してきた

11/18(土)に開催されたJJUG CCC 2017 Fallで1年ぶりに登壇してきました。
タイトルは、「ユニットテストアサーション 流れるようなインターフェースのAssertJを添えて 入門者仕立て」でした。(フレンチのメニューをオマージュしました)
f:id:b1a9id:20171124004247j:plain

50人くらいの部屋だし、11時からだったので半分入ればいいかなと思っていました。でも10分前くらいには既に70人くらい入っていて緊張MAX!!!

何回か練習したから大丈夫と思ってたけどやはり人前に立つと緊張する〜
AssertJ使ったことある人という質問に対して、1割いるかいないかでしたね。素敵なのに案外使われていないことが判明!
自分のレベルの話しでも、AssertJいいなって思ってくださった人がけっこういて非常によかった♪
今回は、大事故スライドなしで挑んでよかったわw(当日の2時まで入ってたけど消した)
ちなみにスライドはこちら。

www.slideshare.net

話して感じたことここまでにして、アンケートで質問等をちょっといただいたのでそれの回答をしようかと思います!

いただいた意見と回答

1.JUnitとの記述の比較がとてもわかりやすかったです。使っている人がまだ少なそうなので、情報を探すのは大変ではなかったでしょうか。そのような点もお話聞かせていただきたいと思いました。

結論から述べると、公式リファレンス読んで、JavaDoc読んで、コード読みました。
まず、AssertJを使おうと思ったきっかけはこの本です!
gihyo.jp

これを見ていた時ちょうど、会社でユニットテストライン80%目指そうぜ計画がはじまりました。
Serviceのテストを書いていて、assertEqualsいっぱいだし、コピペでつまんないってって思っていました。そこでAssertJを書いてみると、書きやすさ、コードの美しさ、エラーメッセージのわかりやすさどれをとっても素敵でした。
AssertJは、1年くらい使ってるのでそれなりの知見はあったのですが、登壇のためにリファレンスを1から読みました。そして実際に手を動かしました。
AssertJの公式リファレンスは、ロードオブザリングとかスターウォーズのキャラクター出てきて案外楽しく読めます!JavaDocは、書き方例が書いてあって理解しやすいです。

2.自分も知らない使い方の紹介もあって良かったです hamcrest matcherとの違いやメソッドチェーンの説明があるともっとわかりやすかったと思いました。

既に使っていらっしゃるんですね。ためになること話せてよかったです。
なお、hamcrest matcherの経験はほぼ皆無(ほぼAssertJしか使ったことない)なのです。
メソッドチェーンの説明とは、それぞれの検証のことですかね?次回に活かします!


以上です。
登壇するとフィードバックがよくても悪くてもとても嬉しいです。
またネタ探して登壇したいと思います!!!

AssertJで流れるようなインターフェースでアサーションを書こう #1

みなさんユニットテスト書いてますか?アサーションライブラリは何を使ってますか?私は、Assert Jが好きで使ってます。

AssertJは、Javaアサーションライブラリで、主にAssertJ CoreとAssertJ SwingとAssertJ DBがあります。
今回は、AssertJ Coreについて書きます。第1回は、概要と導入手順です。
AssertJ Coreは、流れるようなインターフェースでアサーションを書くことができるJavaライブラリで、テストコードを読みやすく改善し、簡単にメンテナンスしやすくすることが主な目的です。
メソッドチェーンで複数の検証を一度に書くことができます。また、JDKの標準タイプのアサーションを提供していて、JUnitTestNGで使うことができます。

バージョン

バージョンは、下の表のようになっています。(2017/10/19時点)3系の大きな特徴は、Project Lambdaを使用した検証ができることです。また、OptionalやDate and Time APIの各クラスの検証もできます。
これらの機能に加えて、2系のすべての機能が利用できます。

Javaバージョン 初期リリース 最新
3系 Java8以上 2015/04/06(3.0.0) 2017/05/21(3.8.0)
2系 Java7以上 2015/03/07(2.0.0) 2017/05/21(2.8.0)
1系 Java6以上 2013/03/26(1.0.0) 2015/01/02(1.7.1)

類似ライブラリ

AssertJ Android

AssertJを拡張したAndroid用のアサーションライブラリです。
square.github.io

Assert Py

AssertJに影響を受けて作られてたPython用のアサーションライブラリです。
github.com

導入手順

依存関係の追加

Spring Boot(>=1.4)を使う場合

その他の場合

  • Gradle

Spring Boot(>=1.4)を使う場合

その他の場合

Spring Bootを使う場合、spring-boot-starter-testには、AssertJが含まれています。しかし、デフォルトで2.6.0が入っているので3.8.0を使う場合は、バージョン指定が必要です。

依存ライブラリがないので、導入がしやすいです。
次回は、実際にどういう風にアサーションが書けるかについてです。

ThymeleafでNumber.javaを継承したクラスをメッセージ式のパラメータとして渡すと千の位で区切ってくれる話

先日、仕事でThymeleafで千の位で区切って表示することをやりました。いわゆる金額表示です。
結論から述べると、以下のようにすると、実現できます。

ちなみにこれはたまたま発見できちゃいました。。。笑
最初は、次のように書いてました。

<p th:text="${#numbers.formatInteger(1234567890, 3, 'COMMA')} + '円'">1,234,567,890円</p>

ある記事を読んでいてメッセージ式にパラメータを渡せることを知りました。
そこでカンマ区切りのことは気にせず、上のように書いてみたら、金額表示になってました。
しかし、なぜ数値をパラメータとして渡すと千の位で区切ってくれる(区切って欲しくないときもあるけど)のか気になったのでソースコード追ってみました。
MessageSourceインターフェースのgetMessageメソッドにブレークポイントを張りました。
MessageFormat.java#subformatが気になりました。

案の定、16行目に止まり、ここでsubformatにDecimalFormat.javaを返していました。
そして、DecimalFormat.javaのsubformatメソッドに処理がうつり、ここで実際に1000の位に区切っていました。それが以下の箇所です。

実際にソースを読んでもらえればわかるのですが、groupingという変数に「,」が格納されています。
DecimalFormatSymbols (Java Platform SE 8 )
ループで回し、パラメタを1個ずつ渡して、1000の位で「,」を入れています。

ちなみに、NumberFormatProvider.javaにこのようなメソッドがあったのでこれをうまく呼ぶ方法を見つけたいなと思いました。

public abstract NumberFormat getCurrencyInstance(Locale locale);

以上です。

Intelli Jのショートカット一覧が日本語訳されました

以前、Intelli Jのショートカットを以下の記事でまとめたのですが、公式で発表されてるのもを日本語訳された会社さんがいらっしゃいました。
uchi-fashion.hatenablog.com

こっちの方が私の拙い日本語訳より参考になると思うのでぜひ!
IntelliJ や PhpStorm などの日本語化

Intelli J ショートカットまとめ

4回にわたってIntelli Jのショートカットをまとめたのですが、みてる自分がめんどくさくなったので1つにまとめました。
Keymapは、Mac OS Xです。異なる方はちょいとキーが変わってくると思います!

Editing
ショートカットキー 説明
^ Space ベーシックなコード補完(いくつかクラスやメソッドや変数の候補をあげる)
^ Shift Space スマートなコード補完(そのメソッドで期待されるタイプの変数の候補をあげる)
Command P パラメータ情報表示
^ J クイックドキュメント表示
Command コードをマウスオーバー 簡単なドキュメント表示
^ N GetterやSetterやConstructorなどのコードを生成
Command O オーバーライドメソッドの表示
Command Option T if...elseやtry...catchなどの雛形コード生成
Command / カーソルがある行のコメントアウト
^ Shift Q コンテキスト情報の表示。puclic class Test extends Hoge { を表示
Command Option L コードフォーマット
^ Shift O import文の整理。使ってないものの削除やソートを行う
Command D カーソルがある行を下に向かってコピペを行う
Command Enter カーソルがある箇所から改行する
Shift Enter カーソルがある行の次の行に空行を追加
Command Shift [ カーソルがある箇所のブロックの始まりからカーソルの箇所まで選択
Command Shift ] カーソルがある箇所からブロックの終わりまで選択
Option → カーソルが単語の終わりに遷移
Option ← カーソルが単語の始まりに遷移
Command W カーソルがある単語を選択
Command Shift U 選択してる単語の大文字小文字切り替え
Command G 開いたモーダルで指定した行数に遷移する
Usage Search
ショートカットキー 説明
Option F7 プロジェクト内でカーソルが当たっているクラス(メソッド)が使われている箇所を表示
Command F7 ファイル内でカーソルが当たっているクラス(メソッド)が使われている箇所を表示
Command Shift F7 ファイル内でカーソルが当たっているクラス(メソッド)が使われている箇所を強調して表示
Command Option F7 プロジェクト内でカーソルが当たっているクラス(メソッド)が使われている箇所を一覧表示
Navigation
ショートカットキー 説明
F12 アクティブウインドウをツールウインドウにする
ESC アクティブウインドウをエディタにする
Shift ESC (直近の)アクティブウインドウを隠す
Command F4 アクティブなタブを閉じる
Command E 最近開いたファイルをポップアップ表示
Command B または Command Click 変数の宣言箇所にカーソルを移動(Command Clickは変数にカーソルが当たってる時)
Command Option B または Command Click 実装箇所にカーソルを移動(Command Clickはメソッドにカーソルが当たってる時))
Command [ コードブロックのはじまりに移動
Command ] コードブロックの終わりに移動
Command F12 ファイル構成(フィールドやメソッド)をポップアップ表示
^ H タイプハイアラーキーを表示
Command Shift H メソッドハイアラーキーを表示
Option ^ H コールハイアラーキーを表示
Search / Replace
ショートカットキー 説明
Shift * 2 どこでも検索
Command F ファイル内検索
Command R 置換
^ Shift F 全ファイルから文字列検索
VCS/Local History
ショートカットキー 説明
^ V バージョン管理システムメニューをポップアップ表示
Refactoring(左ペインで)
ショートカットキー 説明
F5 ファイルのコピー
F6 ファイルの移動
Command Delete セーフデリート
Shift F6 リネーム
Debugging
ショートカットキー 説明
F8 ブレークポイントから一行進める
F7 呼び先のメソッドに入る
F9 次のブレークポイントにとぶ
Shift F8 F7で入ったメソッドから出る
Option F8 ブレークポイントで止まっているときにポップアップウインドウで好きなことを試せる
Command F8 ブレークポイントを解除する
Command Shift F8 ブレークポイントの一覧を表示
ブレークポイントで止まってる時に変数を)Option click 変数の中身が見られる。watchで見られるのと同じ
General
ショートカットキー 説明
Command 1 ~ Command 9 割り当てているツールウインドウを表示
Command ^F フルスクリーンモードにする
Command Shift F12 開いているツールウインドウを全て閉じる
Command , 設定画面開く
Command ; プロジェクトストラクチャーを開く
Others
table>tr>td*2と入力してTabを押す tr * 1、td * 2のテーブルを生成

この中でも1番オススメの機能がこれです!
Option F8:ブレークポイントで止まっているときにポップアップウインドウで好きなことを試せる
f:id:b1a9id:20170707101940p:plain
実際にプロダクトコードで宣言されているものは、vだけです。
変数宣言もできるのでデバッグ能力が格段にあがると思います!
ちなみにAndroid Studioでも使えるらしいのでぜひ試してみてください。

ThymeleafとJSPを共存させた話

転職して、JSPを使っている会社に入りました。今どきJSPはないだろ!っていうことでThymeleafと共存させることにしました。
プロジェクト構成は、こんな感じです。

JavaCofigでBean定義を行うプロジェクト構成

f:id:b1a9id:20170627220712p:plain

xmlでBean定義を行うプロジェクト構成

f:id:b1a9id:20170627234120p:plain

では、さっそくコードを見ていただこうと思います。

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.example</groupId>
	<artifactId>spring-framework-sandbox</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>war</packaging>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<spring.mvc.version>4.3.8.RELEASE</spring.mvc.version>
		<jvl.over.slf4j.version>1.7.25</jvl.over.slf4j.version>
		<logback.classic.version>1.2.3</logback.classic.version>
		<lombok.version>1.16.16</lombok.version>
		<thymeleaf.version>3.0.5.RELEASE</thymeleaf.version>
	</properties>

	<dependencies>
		<!-- Spring MVC -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.mvc.version}</version>
		</dependency>

		<!-- Jvl over slf4j -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${jvl.over.slf4j.version}</version>
		</dependency>

		<!-- logback classic -->
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.classic.version}</version>
		</dependency>

		<!-- Lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>${lombok.version}</version>
		</dependency>

		<!-- Thymeleaf -->
		<dependency>
			<groupId>org.thymeleaf</groupId>
			<artifactId>thymeleaf-spring4</artifactId>
			<version>${thymeleaf.version}</version>
		</dependency>

		<!-- JSP -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.0</version>
		</dependency>

		<!-- JSTL -->
		<dependency>
			<groupId>jstl</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
	</dependencies>
</project>

JavaConfig

ThymeleafConfig.java
@Configuration
public class ThymeleafConfig {

	@Bean
	public SpringResourceTemplateResolver templateResolver(){
		SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
                // テンプレートファイルがどのディレクトリの配下にあるかを指定。ここでは、/WEB-INF/views/配下
		templateResolver.setPrefix("/WEB-INF/views/");
                // 接尾字が何かを指定。ここでは.html
		templateResolver.setSuffix(".html");
                // テンプレートの種類は何かを指定。ここでhtml
		templateResolver.setTemplateMode(TemplateMode.HTML);
		return templateResolver;
	}

	@Bean
	public SpringTemplateEngine templateEngine(){
		SpringTemplateEngine templateEngine = new SpringTemplateEngine();
		templateEngine.setTemplateResolver(templateResolver());
		templateEngine.setEnableSpringELCompiler(true);
		return templateEngine;
	}

	@Bean
	public ThymeleafViewResolver viewResolver(){
		ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
		viewResolver.setTemplateEngine(templateEngine());
		viewResolver.setCharacterEncoding("UTF-8");
                // ViewResolverを使う順序を指定。JSPより先にする。
		viewResolver.setOrder(1);
                // Controllerから返すView名がこの文字列であれば、ThymeleafViewResolverを使う
		viewResolver.setViewNames(new String[] {"thymeleaf/*"});
		return viewResolver;
	}
}
WevMvcConfig.java

JSP用のViewResolverを登録する。Thymeleafとほぼ同じなので説明は割愛します。

@Configuration
@EnableWebMvc
@ComponentScan("com.example")
// ThymeleafConfigをインポートします。
@Import(ThymeleafConfig.class)
public class WebMvcConfig extends WebMvcConfigurerAdapter {

	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		registry.jsp()
				.prefix("/WEB-INF/views/")
				.suffix(".jsp")
				.viewNames("jsp/*");
		registry.order(2);
	}
}

xml

beans-biz.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

	<context:component-scan base-package="com.example"/>

	<bean id="messageSource"
	      class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
		<property name="basename" value="META-INF.messages" />
	</bean>

	<bean id="globalValidator"
	      class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
		<property name="validationMessageSource" ref="messageSource" />
	</bean>
</beans>
beans-webmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

	<context:component-scan base-package="com.example.web.controller" />

	<!-- Spring MVCアノテーション利用設定 -->
	<mvc:annotation-driven validator="globalValidator" />

	<!-- Static Resourceの設定 -->
	<mvc:resources mapping="/resources/**" location="/WEB-INF/views/" />

	<!-- ViewResolverの設定 -->
	<!-- Thymeleaf -->
	<beans>
		<bean id="templateResolver" class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver" >
			<property name="prefix" value="/WEB-INF/views/" />
			<property name="suffix" value=".html" />
			<property name="characterEncoding" value="UTF-8" />
		</bean>
		<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
			<property name="templateResolver" ref="templateResolver" />
		</bean>
		<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
			<property name="templateEngine" ref="templateEngine" />
			<property name="characterEncoding" value="UTF-8" />
			<property name="viewNames" value="thymeleaf/*" />
			<property name="order" value="1" />
		</bean>
	</beans>
	<!-- JSP -->
	<beans>
		<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
			<property name="prefix" value="/WEB-INF/views/" />
			<property name="suffix" value=".jsp" />
			<property name="viewNames" value="jsp/*" />
			<property name="order" value="2" />
		</bean>
	</beans>

</beans>


JavaConfigとxmlの共通のControllerクラスです。
View返すだけのControllerなので、特に説明はいらないかと思います。

WelcomeController.java
@Controller
@RequestMapping("/hello")
public class WelcomeController {

	@GetMapping("/jsp")
	public String helloJsp() {
		return "jsp/hello/index";
	}

	@GetMapping("/thymeleaf")
	public String helloThymeleaf() {
		return "thymeleaf/hello/index";
	}
}

あとは、アプリケーションを起動して次にアクセスするだけです。
http://localhost:8080/hello/jsp
http://localhost:8080/hello/thymeleaf

※ViewResolverの順序は、JSPよりThymeleafを先に指定してください。JSPを先に指定してしまうと、JSP以降に登録されてあるViewResolverを使わなくなってしまいます。

IntelliJ KEYMAP #4(Mac)

4回目は、デバッグと一般的なショートカットです。ちなみにKeymapは、Mac OS Xです。
今回が最終回です!気が向いたらWindowsも書こうかな?(たぶん気が向かない)

Debugging
ショートカットキー 説明
F8 ブレークポイントから一行進める
F7 呼び先のメソッドに入る
F9 次のブレークポイントにとぶ
Shift F8 F7で入ったメソッドから出る
Option F8 Watchと同じ機能をポップアップウインドウで
Command F8 ブレークポイントを解除する
Command Shift F8 ブレークポイントの一覧を表示
General
ショートカットキー 説明
Command 1 ~ Command 9 割り当てているツールウインドウを表示
Command ^F フルスクリーンモードにする
Command Shift F12 開いているツールウインドウを全て閉じる
Command , 設定画面開く
Command ; プロジェクトストラクチャーを開く

以上です。最後の3つは感動しましたw