ようへいの日々精進XP

よかろうもん

俺のツールズ

俺が

俺の為に AWS の EC2 インスタンスや AMI, タグ等を操作する作ったツールをいくつかリリースしている.

start.oreno.tools

ほとんどを

見よう見まねの Go 言語で作っているので, なかなか Go 言語の雰囲気 (ディレクトリ構成やパッケージ管理とかも) に慣れないし, ちゃんとテストも書けていないのでずーっと修行が必要である.

直近で作ったやつは

SSM パラメータストアを操作するやつ

github.com

業務でパラメータストアを操作する必要があったので, これまでのノウハウを最大限に活かすことで二時間位でリリース出来た. この手をツールを作ると必ず公式のドキュメントを斜め読みし, AWS CLI のドキュメントのパラメータに寄せようとするので, 地味にそのサービスの勉強にもなることに気付いた. さらに, 単純な API のラッパーでしかないので, わざわざ Go 言語で書く必要は無いのかなーと思うことがあるが, MacOSLinux, Windows 環境で動かすことが出来たり, インストールの手順も簡素化出来るという点では Go 言語で書くメリットは十分にあると考えている.

今回作った pStore については, まだまだ改修の余地はあるが, 以下のようにパラメータストアに登録しているパラメータの一覧, パラメータの追加, 削除がいい感じで行えるはず.

# パラメータの一覧取得
$ pStore
+-------------------------------------+--------------------------+--------------+---------------------+
|                NAME                 |          VALUE           |     TYPE     |  LASTMODIFIEDDATE   |
+-------------------------------------+--------------------------+--------------+---------------------+
| /123456/88888                       | kawahara-test            | StringList   | 2018-09-29 08:09:43 |
| test.test1                          | ******************       | SecureString | 2018-09-28 22:42:23 |
+-------------------------------------+--------------------------+--------------+---------------------+

# パラメータの追加
$ pStore -put -name="foooooon" -value="baaaaaaarn"

$ pStore
+-------------------------------------+--------------------------+--------------+---------------------+
|                NAME                 |          VALUE           |     TYPE     |  LASTMODIFIEDDATE   |
+-------------------------------------+--------------------------+--------------+---------------------+
| /123456/88888                       | kawahara-test            | StringList   | 2018-09-29 08:09:43 |
| foooooon                            | baaaaaaarn               | String       | 2018-09-29 08:37:53 |
| test.test1                          | ******************       | SecureString | 2018-09-28 22:42:23 |
+-------------------------------------+--------------------------+--------------+---------------------+

# パラメータの削除
$ pStore -del -name="foooooon"
$ pStore
+-------------------------------------+--------------------------+--------------+---------------------+
|                NAME                 |          VALUE           |     TYPE     |  LASTMODIFIEDDATE   |
+-------------------------------------+--------------------------+--------------+---------------------+
| /123456/88888                       | kawahara-test            | StringList   | 2018-09-29 08:09:43 |
| test.test1                          | ******************       | SecureString | 2018-09-28 22:42:23 |
+-------------------------------------+--------------------------+--------------+---------------------+

詳しくは README を見てねとなる.

コマンドラインツールのテストをどうするか

AWS のリソースをどうやって用意するか

AWS のリソースに対してどのようにテストするか. いくつか参考になる記事を既に書かれている方がいらっしゃってとても参考になった.

medium.com

aws.amazon.com

有難うございます.

特に冒頭の記事に紹介されていた dockertest というモジュールは次のタイミングで利用してみたい. (今回は docker-compose を利用している)

github.com

ということで, 今回のテストについては AWS 環境は moto を利用することにした.

github.com

moto には moto_server というサーバーモードが用意されているので, このサーバーモードを利用して擬似的に API レスポンスを返す環境を用意した. また, moto_server は docker-compose を利用して起動するようにしてみた.

コマンドの出力結果をテストする

コマンドラインツールの出力をテストする場合, どのような方法が良いのかは以下の記事やスライドがとても参考になった.

出力結果をテストするということは, I/O に依存するテストということになるらしい.

I/O に依存するテストの例として, Ruby の場合だと StringIO クラスを利用してテストを書くと良いようだ.

require "rspec"

describe "stdout をテストする." do
  it "hello が出力されること." do
    $stdout = StringIO.new
    puts "hello"
    output = $stdout.string
    expect(output).to eq("hello\n")
    $stdout = STDOUT
  end
end

これを実行すると, 以下のように出力される.

$ rspec test_spec.rb --format documentation

stdout をテストする.
  hello が出力されること.

Finished in 0.00374 seconds (files took 0.18315 seconds to load)
1 example, 0 failures

Go 言語だと bytes.Buffer を利用すると良いとのこと.

package main

import (
    "bytes"
    "os/exec"
    "testing"
)

func TestCommand(t *testing.T) {
    cmd := exec.Command("echo", "hoge")
    stdout := new(bytes.Buffer)
    cmd.Stdout = stdout

    _ = cmd.Run()

    if stdout.String() != "hoge\n" {
        t.Fatal("Not matched")
    }
}

これを実行すると, 以下のように出力される.

$ go test -v
=== RUN   TestCommand
--- PASS: TestCommand (0.01s)
PASS
ok      go-test 0.016s

ということで, pStore のテストは

実行してみると, 以下のような出力になる.

$ make test
Creating network "tests_default" with the default driver
Creating moto-server ... done
=== RUN   TestVersionFlag
--- PASS: TestVersionFlag (1.54s)
=== RUN   TestStdoutList
--- PASS: TestStdoutList (1.60s)
=== RUN   TestStdoutPut
--- PASS: TestStdoutPut (3.33s)
=== RUN   TestStdoutDel
--- PASS: TestStdoutDel (3.33s)
=== RUN   TestStdoutPutSecure
--- PASS: TestStdoutPutSecure (4.43s)
=== RUN   TestStdoutPutList
--- PASS: TestStdoutPutList (4.93s)
=== RUN   TestConvertDate
--- PASS: TestConvertDate (0.00s)
PASS
ok      pStore  19.186s
Stopping moto-server ... done
Removing moto-server ... done
Removing network tests_default

ただ, これだけなんだけど.

以上

永遠の初心者が「俺のツールス」を通して, 少しでもプログラミングのスキルが上がると嬉しいなと思っている.