Slide 1

Slide 1 text

SCALA の SCALA の UNIT テスト UNIT テスト で でDOCKER DOCKER @yoshiyoshifujii 1

Slide 2

Slide 2 text

こんにちは こんにちは Yoshitaka Fujii Software Engineer Scala / DDD / Microservices / Serverless / Agile @yoshiyoshifujii

Slide 3

Slide 3 text

2

Slide 4

Slide 4 text

UNIT テスト UNIT テスト で毎回、苦労 で毎回、苦労 するところ するところ 3

Slide 5

Slide 5 text

データベースを操作するプログラムのテスト Mock とかにしちゃいがち ElasticSearch Amazon Simple Queue Service 様々な外部サービス/ ミドルウェア 4

Slide 6

Slide 6 text

開発のとき 開発のとき git clone して test ができない 環境構築手順書が必要(Windows 版、macOS 版…) 動かないスクリプト 共通のサーバー 5

Slide 7

Slide 7 text

DEPLOY のとき DEPLOY のとき Jenkins Slave に事前にインストール インストールできない場合は接続できるように 版が変わったらSlave の環境を更新 職人の誕生 開発環境と別で対応する必要がある 開発環境と共存にすると、ルールでしばったり大変 6

Slide 8

Slide 8 text

理想 理想 Unit テスト実行したら環境できて欲しい 開発もCD/CI も実行したら環境できて欲しい git clone したら何も考えずに test 実行したい 7

Slide 9

Slide 9 text

DOCKER ならできそう DOCKER ならできそう docker コマンドを実行したらimage 取れるし docker コマンドを実行したら環境できるし Unit テストのbefore/after でなんとかなりそう 8

Slide 10

Slide 10 text

探したらあっ 探したらあっ た た whisklabs/docker-it-scala 9

Slide 11

Slide 11 text

SETUP SETUP libraryDependencies ++= Seq( "com.whisk" %% "docker-testkit-scalatest" % "0.9.5" % "test", "com.whisk" %% "docker-testkit-impl-spotify" % "0.9.5" % "test" ) 10

Slide 12

Slide 12 text

ちなみに… ちなみに… は、 は、 に に 依存しています。 依存しています。 は、Java ライブラリなので、こ は、Java ライブラリなので、こ れを使ってうまいことやれば、Java で出来る。 れを使ってうまいことやれば、Java で出来る。 ( はずです。試してません…) ( はずです。試してません…) whisklabs/docker-it-scala whisklabs/docker-it-scala spotify/docker-client spotify/docker-client spotify/docker-client spotify/docker-client 11

Slide 13

Slide 13 text

今回はDynamoDB で試してみた 今回はDynamoDB で試してみた libraryDependencies ++= Seq( "com.amazonaws" % "aws-java-sdk-dynamodb" % "1.11.353" ) 12

Slide 14

Slide 14 text

DockerSpecSupport.scala DockerSpecSupport.scala Spotify のDocker Client を使って Spotify のDocker Client を使って docker を操作する基底となる処理を書く。 docker を操作する基底となる処理を書く。 trait DockerSpecSupport extends DockerTestKit { this: Suite => private val dockerClient: DockerClient = DefaultDockerClient.fromEnv().build() override implicit def dockerFactory: DockerFactory = new SpotifyDockerFactory(dockerClient) } 13

Slide 15

Slide 15 text

DockerDynamoDBSpecSupport.scala DockerDynamoDBSpecSupport.scala trait DockerDynamoDBSpecSupport extends DockerSpecSupport { this: protected val dynamoDBPort = RandomSocket.nextPort() protected val dynamoDBContainer = DockerContainer("fingershock/dynamodb-local:latest") .withPorts(8000 -> Some(dynamoDBPort)) protected val dynamoDBClient: AmazonDynamoDB = AmazonDynamoDBClientBuilder.standard() .withEndpointConfiguration( new EndpointConfiguration( s"http://127.0.0.1:$dynamoDBPort", Regions.AP_NORTHEAST_ )).build() } 14

Slide 16

Slide 16 text

DynamoDBSpec.scala DynamoDBSpec.scala class DynamoDBSpec extends FlatSpec with Matchers with DockerDynam private val TableName: String = "sample-table" override def dockerContainers: List[DockerContainer] = dynamoDBContainer :: super.dockerContainers override def beforeAll(): Unit = { ... } override def afterAll(): Unit = { ... } "DynamoDB" should "success" in { ... } } 15

Slide 17

Slide 17 text

DynamoDBSpec.scala #beforeAll DynamoDBSpec.scala #beforeAll override def beforeAll(): Unit = { super.beforeAll() val createTableRequest = new CreateTableRequest() .withTableName(TableName) .withAttributeDefinitions( new AttributeDefinition("id", ScalarAttributeType.S)) .withKeySchema( new KeySchemaElement("id", KeyType.HASH)) .withProvisionedThroughput( new ProvisionedThroughput(1L, 1L)) dynamoDBClient.createTable(createTableRequest) } 16

Slide 18

Slide 18 text

DynamoDBSpec.scala #afterAll DynamoDBSpec.scala #afterAll override def afterAll(): Unit = { dynamoDBClient.deleteTable(TableName) super.afterAll() } 17

Slide 19

Slide 19 text

DynamoDBSpec.scala #DynamoDB should success DynamoDBSpec.scala #DynamoDB should success "DynamoDB" should "success" in { dynamoDBClient.putItem( TableName, Map( "id" -> new AttributeValue().withS("aaaaa") ).asJava ).getAttributes shouldBe null dynamoDBClient.getItem( TableName, Map( "id" -> new AttributeValue().withS("aaaaa") ).asJava ).getItem.asScala("id").getS shouldBe "aaaaa" } 18

Slide 20

Slide 20 text

run run 21:31:58.171 [pool-5-thread-1] DEBUG com.spotify.docker.client.Do 21:31:59.184 [pool-5-thread-1] DEBUG com.spotify.docker.client.Do 21:32:01.330 [pool-7-thread-1] DEBUG com.spotify.docker.client.au java.lang.IllegalArgumentException: serverAddress=https://index.d at com.spotify.docker.client.DockerConfigReader.parseDock at com.spotify.docker.client.DockerConfigReader.fromConfi at com.spotify.docker.client.auth.ConfigFileRegistryAuthSu at com.spotify.docker.client.DefaultDockerClient.pull(Def at com.spotify.docker.client.DefaultDockerClient.pull(Def at com.whisk.docker.impl.spotify.SpotifyDockerCommandExecu at com.whisk.docker.impl.spotify.SpotifyDockerCommandExecu at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0 at scala.concurrent.Future$.$anonfun$apply$1(Future.scala at scala.concurrent.Future$$$Lambda$3750/1704912779.apply at scala.util.Success.$anonfun$map$1(Try.scala:251) at scala util Success map(Try scala:209) 19

Slide 21

Slide 21 text

まとめ まとめ を使えば、 を使えば、 Unit テストからDocker Image を実行できる。 Unit テストからDocker Image を実行できる。 は、Java ライブラリなので、 は、Java ライブラリなので、 きっと同じことできる。 きっと同じことできる。 git clone したら test がすぐできる。 git clone したら test がすぐできる。 CD/CI のときも、環境作らなくて良い。 CD/CI のときも、環境作らなくて良い。 サンプルコードは、 サンプルコードは、 whisklabs/docker-it-scala whisklabs/docker-it-scala spotify/docker-client spotify/docker-client yoshiyoshifujii/180623_using_docker_in_unit_test_of yoshiyoshifujii/180623_using_docker_in_unit_test_of 20

Slide 22

Slide 22 text

以上です 以上です 21