体制の話

  • 依存性が内部持ちになっていたのをArtifactory経由に修正
  • スクリプトのレビュー
  • 体制の話
  • よーさん、ふーたくさん、かせさんとランチ

BATSでbashスクリプトのテストをする #bats #bash

概要

bashスクリプトのテストコードを書く方法がないか調べていたら、BATSというのが見つかって中々よかった。

github.com

下記の記事を鵜呑みにして試してみてたのだけれど、sstephensonが有名人であることが上司からの突っ込みで発覚した。
GitHubちゃんと見ると、rbenvとかbasecampの文字がありましたねw)

postd.cc

インストール

Macだとbrewで導入可能。

brew install bats

実行方法

テストはbatsという拡張子のファイルを作って、batsコマンドに渡すだけ。

bats test_sample.bats

batsファイル

下記のような感じで書く。

setupとteardownが用意されているのはうれしい。

#!/usr/bin/env bats

# ケース実行前の処理
# 処理が何もない場合エラーになるので、必要なときだけ実装する
setup() {
  mkdir test_dir
}

# ケース実行後の処理
# 処理が何もない場合エラーになるので、必要なときだけ実装する
teardown() {
  rmdir test_dir
}

@test "test sample" {
  # テスト対象処理はrunで実行する
  # runで実行することで、BATSの特殊変数等を利用できる
  run ./sample.sh

  # assertはテストコマンドで行う
  # status変数にはrunで実行したコマンドの終了ステータスが格納される
  [ "${status}" -eq 0 ]
  # output変数にはコマンドの出力内容が格納される
  [ "${output}" = "Success!" ]
  # lines変数には行毎に配列でコマンドの出力内容が格納される
  [ "${lines[0]}" = "Success!" ]
}

runでは知らせると、status/output/linesにそれぞれ値が格納される。

出力内容やステータスが代入されるので、その値をアサーションしていく。

外部スクリプトの読み込み

テストのヘルパー的なものや、テスト対象が関数に絞られる場合、loadを使って読み込みできる。

loadした後は関数だけrunすることもできるし、テストメソッド内で単純に実行もできる。

load core_function
load test_helper

拡張子がbashの場合、相対パスかつ拡張子なしで記載する。

拡張子が異なる場合はフルパスで記載する必要がある。

なお、loadはテストメソッド内外どちらでも利用可能。

関数と実処理を分離する

関数だけのテストをしたい場合、loadでfunctionを読み込むことになると思う。

しかしload は結局 source みたいなものなので、load時点でスクリプトが実行されてしまう。

もし既存のコードに部分的にテストを足したい場合、関数と実行処理を分ける方針にするといいかもしれない。

Mock

Mockが必要になるような複雑な処理までbashでがんばるなとは思うけど、現実世界はどうにも厳しいことがあると思う。

コマンドや関数をMock化したい場合は同名の関数を定義して上書きするとよい。

curl() {
  echo "curl command is called."
  return 0
}

上述の場合、curlコマンドを上書きしている。

通信はせず標準出力した上で常に0を返すようになっている。

関数定義はテストメソッド内外どちらでも可能だけれど、基本的にはテストメソッドに閉じる形にしておくのがよいと思う。

BATSの動き的にはテストメソッド毎に関数定義は読み直してくれるようなのであまりないと思うが、Mockを元に戻すには unset コマンドを使えばよい。

unset curl

CI

CircleCIでテストを走らせるようにしてみた。

2.0のクラシックイメージにはそもそもBATSが入っているようで、特にインストール等は必要なかった。

config.ymlは下記のような感じ。

version: 2
jobs:
  build:
    docker:
      - image: sukun1899/bats
    steps:
      - checkout
      - run: bats -t .

まとめ

  • bashでもテスト書ける
  • でもbashで無理すんな

参考

いろいろ試したのは下記に置いてある。

github.com

RSpecよりMiniTestの方が好きだと思ったのはなんでか考えてみた

最近、社内でモブプロの交流会等で、RSpecとMiniTest両方を触る機会があった。

どちらもガッツリ触ったわけではないのだけれど、直感的にMiniTestの方が好きだなぁと感じて、なんでかなぁって考えてみた。

RSpecはこんな感じ。

RSpec.describe Order do
  it "sums the prices of its line items" do
    order = Order.new

    order.add_entry(LineItem.new(:item => Item.new(
      :price => Money.new(1.11, :USD)
    )))
    order.add_entry(LineItem.new(:item => Item.new(
      :price => Money.new(2.22, :USD),
      :quantity => 2
    )))

    expect(order.total).to eq(Money.new(5.55, :USD))
  end
end

MiniTestはこんな感じ。

class TestMeme < MiniTest::Unit::TestCase
  def setup
    @meme = Meme.new
  end

  def test_that_kitty_can_eat
    assert_equal "OHAI!", @meme.i_can_has_cheezburger?
  end

  def test_that_it_will_not_blend
    refute_match /^no/i, @meme.will_it_blend?
  end

  def test_that_will_be_skipped
    skip "test this later"
  end
end

パッと見た感じだと、RSpecの方がテストしたいことが分かりやすい気はする。

構文とか覚えるコストはMiniTestの方が低そう。

ただどちらにせよ、好き嫌いの判断に大きな影響を与えていない気がする。

テストコードで知ってるの並べてみる

  • 好き
  • 嫌いではない
  • そんなに好きじゃない

触った程度しかないものもあるけど、並べてみる。

BDD系があんまり好きでなくてxUnit系が好きなのかな。

大好きなSpockはBDDなのかはよく分からないw

ただRSpecにインスパイアされたみたい*1ではある。

掘り下げてみる

もう少し自分の考えを深掘りしてみたら、なんとなく思った。

RSpecとかBDD系は「What」に軸をおいたテストなんじゃないかなぁと。

それこそ「振る舞い」なのだから、実現したい「こと」にフォーカスしてるというイメージ。

Howをテストしたい

んで、自分がテストを書く時何を考えているかというと、どちらかといえば 「How」じゃないかなぁと。

もちろんゴールは「What」なんだけど、その実現したいことを「この方法でうまくいくかなぁ」とか「この書き方はどうだろうか」とか設計も含めた実現する手段の整理もしている気がする。

するとテストコードというと僕は(とりわけTDDやるときのような)UnitTestをイメージしてるんだなってことに気付く。

まとめ

UnitTestを書く時はプロダクションコードもセットで考える(Howを考える)ので、Howにフォーカスしやすいテストコードを書きたくなるんだと思う。

んで、RSpecはWhatにフォーカスしている(ように感じた)から、MiniTestの方が好きな感じがしたのかもしれない。

なんだか、各方面に叩かれそうだな。。

いい悪いの話ではなく、好き嫌いの話なのでご容赦ください。

TDDBC Tokyo 2017-09に参加してきた #tddbc

TDDBC Tokyo 2017-09に参加してきた。

レポートというより、感じたことや考えたことなど。

tddbc.connpass.com

当日の流れ

  1. 基調講演
  2. ペアプロデモ
  3. ペア決め&ランチ
  4. ペアプロしつつ合間に成果発表的なコードレビュー
  5. 懇親会

基調講演

www.slideshare.net

「バグのモグラ叩きでは多様化する品質には対応できない。プログラムの品質向上がますます求められている。」という話がとても印象的だった。

作りきりじゃなくて継続開発していくものが多くなっているし、新しい技術もどんどん出てくるし、内部品質を上げることの価値はどんどん高まっているのだなぁと。

精進しなくては。

テストファースト、自分も必ずしもやっているわけじゃないけど、ゴールが明確になるのがいいと思っている。

あとステップを細かくできたりとか。

たくさん考えて検討するのすごいなぁと思うけど、作ってみてから分かることの方が多いと思っているので、小刻みに進めていったほうがいいなぁと。

書いてて思ったけど、そうかTDDも学習サイクルだしフィードバックループなんだなぁ(今更再確認)。

ペアプロデモ

f:id:su-kun1899:20171002004343p:plain

ペアプロのデモでドライバーとナビゲータの役割について質問が出てた。

一緒に作業する時、人はなぜ役割を求めてしまうのか。

ドライバーとナビゲータってまぁひとつの分かりやすいモデルなんだろうけど、個人的には役割とかそんなに気にしなくていいと思うんだよなぁ。

会話を止めないこととか、気軽に話せるようにするとか、そういうことの方が大事かなと。

ペア決め&ランチ

Java+Spockでやりたいけど、そういう人いないかなぁって思ってたら、同じ思いの人がいてペアになれた!

Mavenで環境作ってGitHubに上げてたのを使ってくれたので、無駄にならずに済んでよかったw

Java勢もおおかったけど、Groovy勢が多くて何だかうれしかったなぁと。

ランチもScalaの人とかJVM界隈の人たちと同じテーブルで話せて楽しくて、普段は参加しない懇親会に参加を決めるきっかけになったのだった。

ペアプロ

お題はセマンティックバージョニングだった。

お題: セマンティック・バージョニング · GitHub

僕らのペアはマシンは別にしてgithubでpush/pullで交代することにした。

モブでもペアプロでも一台のマシンで、っていうのがセオリーみたいだけど、大体みんな自分の環境にこだわりをもっているから、それを活かしたほうがいいこともいっぱいあると思う。

IDEの知らない操作教え合ったり、Groovyでメソッドまでパラメータ化しちゃうやつ教えてもらったり、学びもいろいろあった。

Spockいいですよねぇ、って話しながらペアプロするの楽しかったw

成果発表的なコードレビュー

ペアの何組かをピックアップして、成果発表的なコードレビューが行われた。

みんな喋りがうまいなぁという感想w

いろんな言語のコードとアプローチを聞けるのは楽しかった。

言語によって気にすることとか全然違うのだなぁというのはC言語の発表を聞いて感じた。

そう考えるとGo言語のように最初からテストの仕組みがあるのは強いだろうなとは思う。

後発言語なので当たり前なのかもしれないけど、どんどん世界は進歩していっているんだなぁ。

懇親会

懇親会ではスライドなんてなくてもよいので、飛び入り可能なLT大会があったので、(ビールの力を借りて)参戦した。

テスト絡みで何かネタあるかなぁ、と探していたら過去にモブプロとテストのことブログに書いてたのでその辺のことを話してみた。

su-kun1899.hatenablog.com

  • テストコードはレビューコストが高い
  • モブプロでやるとテストの質自体もあがるし、レビューしながらやれる
  • モブプロ最高

ってことがうまく伝わっていたらいいなぁ。

まとめ

僕はテストコード書くのが好きだし、楽しいなぁっていうのを改めて再確認するなど。

懇親会に出てみようと思えたのとか、LT飛び入りしようとか、質問がしやすいなとか、運営の方々と参加者の皆さんががそういう雰囲気を作り出してくれていたんだと思う。

たくさん刺激をもらえてとても充実した一日でした。

なお、僕はIntelliJ IDEAアルティメット使っています。そしてSpockは最高です。

f:id:su-kun1899:20171001130126j:plain

MySQLのConnector/J (JDBC)のconnectTimeoutとsocketTimeout #MySQL

MySQLのConnector/J (JDBC)にはconnectTimeoutとsocketTimeoutのパラメータがあり、JDBCの接続文字列に追加することで設定できる。

MySQL :: MySQL Connector/J 5.1 Developer Guide :: 5.1 Driver/Datasource Class Names, URL Syntax and Configuration Properties for Connector/J

設定例

値はどちらもミリ秒で設定できる。

どちらも30秒で設定する場合、下記のようになる。

jdbc:mysql://localhost:3306/sample?connectTimeout=30000&socketTimeout=30000

socketTimeoutの注意事項

socketTimeoutはアプリケーションからMySQLの応答が設定値を超えて待機すると、java.net.SocketTimeoutException に類する例外が発生するようになる。

こんな感じ。

Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet successfully received from the server was 30,031 milliseconds ago.  The last packet sent successfully to the server was 30,030 milliseconds ago.

ここで注意しないといけないのは、単純に重いクエリを投げた場合でも、SocketTimeoutとして扱われてしまうこと。

例えばバッチ処理なんかで30秒以上かかるクエリをアプリから放り投げると例外が発生してしまう。

感想

DBはDBで死活監視、アプリはアプリで死活監視を入れてあるのを前提とすると、JDBCのパラメータでタイムアウト設定するよりは、アプリ側で使っているDBCP管理のAPIを経由する使うのがよいと思う。

ConnectTimeoutの設定値は大体ある気がするし、DBの死活監視がされているならアプリ側ではクエリタイムアウトの管理でよいかと思う。

JDBCのパラメータまで使い始めると、ただでさえ繁雑になりがちなタイムアウト値の管理がもっと複雑になってしまうような気がする。

mysqlのdockerイメージを使ってみた #MySQL #docker

概要

DockerHubにあるMySQLの公式イメージを使ってみた。

※Docker for Mac使用

https://hub.docker.com/_/mysql/

イメージを持ってくる

5.6のイメージを使ってみることにする。

$ docker pull mysql:5.6

ダウンロードを確認

$ docker images

起動する

とりあえず13306ポートでrunしてみる

docker run \
  -d \
  --name mysql-docker \
  -p 13306:3306 \
  --env MYSQL_ALLOW_EMPTY_PASSWORD=yes \
  mysql:5.6

MYSQL_ALLOW_EMPTY_PASSWORD=yes としておくことで、パスワード無しで接続できる。

dockerで初期設定っぽいものをパラメータ的に渡すのは環境変数を使うのがセオリーなのかな?

mysqlコマンドで繋いでみる

MySQLはデフォルトポート以外を使う場合、localhostでは接続できないので、IPでつなぐ。

$ mysql -u root -h 127.0.0.1 -P 13306
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.37 MySQL Community Server (GPL)

設定ファイルを使う

自前の設定を使う場合、設定ファイルの配置場所をDocker側にマウントさせる。

たとえば設定ファイルが /my/custom/config-file.cnf の場合なら --volume /my/custom/config-file.cnf:/etc/mysql/conf.d を指定する。

volumeオプションは相対パスだとうまく動かなくて、どうやら絶対パスじゃないとダメなようだ。

docker run \
  -d \
  --name mysql-docker \
  -p 13306:3306 \
  --env MYSQL_ALLOW_EMPTY_PASSWORD=yes \
  --volume /my/custom/config-file.cnf:/etc/mysql/conf.d \
  mysql:5.6

mysqlにつないだら show variables とかで設定ファイルの内容が反映されているか確認できる。

パス系の設定は注意

無邪気に出来合いの設定ファイルを渡すとうまく動かなかったり、コンテナが終了したりする。

ログファイルの場所など、パスが絡む設定はそのままだと使えなそう。

とりあえず必要ならコンテナの中に入って参照すればいいと思うので、渡す設定からは外すことにした。

まとめ

他にも起動時にデータベースを作成することもできたり、初期SQLを流し込んだりもできるみたい。

公式のイメージを使ってやりたいことは一通りできそうなので、とても便利である。

忘れそうなのでGitHubに残しておいた。

GitHub - su-kun1899/mysql: mysql 関連の置き場

参考

dqn.sakusakutto.jp

Macにdelveを導入する #golang

goのデバッグができるというdelveの導入を試みる。

基本こいつにしたがってやってみた。

github.com

brewしてみる

$ brew install go-delve/delve/delve

キーチェインに証明書を追加

こちらを参考に。

github.com

dlv-cert.cfg を作成

[ req ]
default_bits            = 2048                  # RSA key size
encrypt_key             = no                    # Protect private key
default_md              = sha512                # MD to use
prompt                  = no                    # Prompt for DN
distinguished_name      = codesign_dn           # DN template
[ codesign_dn ]
commonName              = "dlv-cert"
[ codesign_reqext ]
keyUsage                = critical,digitalSignature
extendedKeyUsage        = critical,codeSigning

鍵を作ってキーチェインに追加

$ openssl req -new -newkey rsa:2048 -x509 -days 3650 -nodes -config dlv-cert.cfg -extensions codesign_reqext -batch -out dlv-cert.cer -keyout dlv-cert.key
$ sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain dlv-cert.cer
$ sudo security import dlv-cert.key -A -k /Library/Keychains/System.keychain

インストールを確認

$ dlv version
Delve Debugger
Version: 1.0.0-rc.1
Build:

VSCodeで使いたい

どうも使えるらしいのだけれど、なんかうまく行かなくて挫折した。

↓の記事を参考にやってみてもいいかもしれない(試してない) dev.classmethod.jp

VSCodeで発生したエラー

could not launch process: exec: “lldb-server”: executable file not found in $PATH

このエラーはxcode-selectをインストールすると直るらしい。

$ xcode-select --install