Skip to content

Instantly share code, notes, and snippets.

@suzuki-hoge
Created June 23, 2017 04:03
Show Gist options
  • Save suzuki-hoge/2ff7b5901ac5068350b7ef4e2f66c1c5 to your computer and use it in GitHub Desktop.
Save suzuki-hoge/2ff7b5901ac5068350b7ef4e2f66c1c5 to your computer and use it in GitHub Desktop.
class MoreResultTest extends Specification {
private static Result result = new Result(
Parser.parse(ParserTest.bobio)
)
def test_enum() {
expect:
result.as('status', { it == '1' ? Status.OK : Status.NG }) == Status.OK
}
def test_StartDate() {
expect:
result.as('start', DateFormatter.parse_yyyymmdd.andThen(StartDate.&of)) == new StartDate(LocalDate.of(2017, 6, 15))
}
}
@Unroll
class AnotherResultTest extends Specification {
private static AnotherResult result = new AnotherResult(
Parser.parse(ParserTest.bobio)
)
def get_ok() {
when:
String r = result.key('code').get()
then:
notThrown(RuntimeException)
r == '0'
}
def get_ng() {
when:
result.key('invalid').get()
then:
def e = thrown(RuntimeException)
e.message == 'no such key: invalid'
}
def getOr() {
expect:
result.key(key).getOr(or) == exp
where:
key | or || exp
'code' | '0' || '0'
'code' | '99' || '0'
'invalid' | '99' || '99'
}
def opt() {
expect:
result.key(key).opt() == exp
where:
key || exp
'code' || Optional.of('0')
'invalid' || Optional.empty()
}
def map_get_ok() {
when:
Code r = result.key('code').map(Code.&of).get() // 型を見失ってエディタに警告されてしまうな...
then:
notThrown(RuntimeException)
r == new Code('0')
}
def map_get_ng() {
when:
Code r = result.key('invalid').<Code> map(Code.&of).get() // こうすると警告は消える
then:
def e = thrown(RuntimeException)
e.message == 'no such key: invalid'
}
def map_or() {
expect:
result.key(key).<Code> map(Code.&of).getOr(or) == exp
where:
key | or || exp
'code' | new Code('0') || new Code('0')
'code' | new Code('99') || new Code('0')
'invalid' | new Code('99') || new Code('99')
}
def map_opt() {
expect:
result.key(key).<Code> map(Code.&of).opt() == exp
where:
key || exp
'code' || Optional.of(new Code('0'))
'invalid' || Optional.empty()
}
def map_enum() {
expect:
result.key('status').map({ it == '1' ? Status.OK : Status.NG }).get() == Status.OK
}
def map_StartDate() {
expect:
result.key('start').map(DateFormatter.parse_yyyymmdd.andThen(StartDate.&of)).get() == new StartDate(LocalDate.of(2017, 6, 15))
}
def map_map_StartDate() {
expect:
result.key('start').map(DateFormatter.parse_yyyymmdd).map(StartDate.&of).get() == new StartDate(LocalDate.of(2017, 6, 15))
}
}
@Unroll
class MapTest extends Specification {
private static Map<String, String> map = [
'code' : '0',
'status' : '1',
'number' : '1',
'message': '50% => 80%',
'start' : '20170615'
]
def get_ok() {
expect:
map.get('code') == '0'
}
def get_ng() {
expect:
map.get('invalid') == null
}
def getOr() {
expect:
? == exp
where:
key | or || exp
'code' | '0' || '0'
'code' | '99' || '0'
'invalid' | '99' || '99'
}
def opt() {
expect:
? == exp
where:
key || exp
'code' || Optional.of('0')
'invalid' || Optional.empty()
}
def get_code_ok() {
expect:
new Code(?) == new Code('0')
}
def get_code_ng() {
expect:
new Code(?) == new Code(null)
}
def code_or() {
expect:
? == exp
where:
key | or || exp
'code' | new Code('0') || new Code('0')
'code' | new Code('99') || new Code('0')
'invalid' | new Code('99') || new Code('99')
}
def code_opt() {
expect:
? == exp
where:
key || exp
'code' || Optional.of(new Code('0'))
'invalid' || Optional.empty()
}
def test_enum() {
expect:
? == Status.OK
}
def test_StartDate() {
expect:
? == new StartDate(LocalDate.of(2017, 6, 15))
}
}

追加お題

1 一時変数と for の排除

お題

  • Parser.javaにおいて、処理用の一時変数およびforの利用を禁止する

提示したテストの修正・追記

  • なし

意図

  • Stream処理になれる
  • 一時変数の排除について考える

2 enum 対応

お題

  • asを使って enum 変換をする

提示したテストの修正・追記

  • レガシー感を出すため、ちょっとbobioを修正
    • status=okstatus=1に変更
  • Status.javaを適当に作成
  • MoreResultTest.groovyを作成

意図

  • asの第2引数は別にコンストラクタのためにあるわけでは無いことを理解する
  • String -> Enumの変換もハマる事を理解する

3 date-time 対応

お題

  • Stringを受けるとLocalDateにして返す関数を作成する
    • DateFormatter.javaとする
    • ヒント: Javaはメソッドは関数合成出来ないので、最初から関数で用意しておく
    • ヒント: andThenFunctionのメソッド
  • asを使って date-time 変換をする

提示したテストの修正・追記

  • 日付の項目をbobioに追加
    • start=20170615
  • StartDate.javaを適当に作成
    • フィールドはLocalDate
  • MoreResultTest.groovyを作成

意図

  • asの第2引数は別にコンストラクタのためにあるわけでは無いことを理解する
  • String -> LocalDate -> StartDateの変換もハマる事を理解する
  • 関数合成を知る

4 別方法

お題

  • AnotherResult.javaを作成し、別の方法で同じ様な事を実現する
    • 先にキーを指定し、任意回数のmapをしたあとに任意の方法で取り出す
    • ヒント: Optional
  • 加工と取り出しを分けるため、加工しつつ取り出すasは廃止し、かわりに加工だけを行うmapを設ける
  • enum 変換、date-time 変換も行う

提示したテストの修正・追記

  • AnotherResultTest.groovyを作成

意図

  • 任意回数の変換処理を行う方法を考える
  • 内部クラスを扱う

5 手書き比較

お題

  • Result.javaおよびAnotherResult.javaの様なラッパーを使わずに、Mapを直接扱うテストを書く

提示したテストの修正・追記

  • MapTest.groovyを作成
    • ただしテストに直接Map処理のコードを書くことになるので、一部伏せ字とする
    • 必要なテストが出来れば、必ずしもサンプルと全く同じ行や内容にならなくても良い

意図

  • ラッパーとの違いを考える機会を作る
  • ラッパーが手に馴染んだかを知る
    • ラッパー無しが楽だと思うか、ラッパーを作るコスト < ラッパーの使いやすさだと思うか
    • 個人差や状況差はあるが

6 考察

お題

  • 一時変数の排除について、他実施者と議論する
  • ラッパーのメリットデメリット、その他考察を他実施者と議論する

提示したテストの修正・追記

  • なし

意図

  • 一時変数について考えて欲しい
  • 目線移動について考えてみて欲しい
  • 改行位置について考えてみて欲しい
  • プリミティブの排除について考えてみて欲しい
  • nullの排除について考えてみて欲しい
@suzuki-hoge
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment