サーバー実行プログラムの時間軸テストフレームワーク案


# サーバー実行プログラムの時間軸テストフレームワーク案

連休中の自由研究です。

## これは何？

 * 時間により振る舞いが異なるプログラムを、サーバーごと、時間を変更しながらテストするテストフレームワークの案です。
 * プロトタイプを簡単に実装し、デモを作ってみましたのでその紹介もあります。

## 背景
サーバー内のプログラムは時間との関係で次のような動作・ふるまいをすることがあります。

 * 一定の時間に実行される。
  例えばATコマンドで日時指定されて実行されるプログラム。

 * 一定の時間ごとに実行される。
  例えばcronで10分間隔で実行される設定をされているプログラム。

 * 一定の時間では同じプログラムが違う振る舞いをする。
  例えば設定された時間中にはHTML出力の中に注意書きを挿入するようコーディングされているPHPプログラム。

こういった時間にかかわるプログラムに関して、サーバーごと使い、その時間のふるまいをテストできるテストフレームワークがないように思われます。（勉強不足で知らないだけかもしれません。ご存知なら教えてください。。）
 
### 自動化テストフレームワークの例
 * 以下のような種類と実装がありますが、サーバーをまるごとつかった時間軸でテストするものは見つけられませんでした。
  
  * 1) ユニットテスト系
     JUnit, PHPUnitとかのいわゆるxUnit系。 PerlのTest::More。RSpecとかか。

  * 2) WEB UIテスト 統合テスト系
      Selenium WebDriver
      Capybaraもこのあたり？

  * 3) サーバー設定テスト系
      Serverspec
      Infrataster

 * 3)のようにサーバをまるごとつかっていて、テストする対象は1)2)で時間による振る舞いをテストできるような感じかな。

 * 昨今の開発環境、ツール類といった状況もこういった汎用的なものをつくり、実用にすることを可能になってきました。
  * 開発環境のハードウェアが進歩した。そのため各開発者ごとに開発用サーバーをVM環境でもてる。
  * Chef, Ansible等のサーバー構成管理ツールの発達で、本番サーバーと同じ環境を手軽に作れる。
   いじり倒して壊れたり、いらなくなったりすればすてて簡単に再作成できる。

  * => 時間軸テスト用のサーバーを立てて好き勝手に時間を変更してテストし、おかしくなっても
  捨てて新たにサーバーを簡単に立て直す事ができる。

まとめると下のようなもの。

## 目的

 * cron設定により、あるいはそれ以外の要因により、一定の時間に実行されるプログラムを対象とする。
 * 一定の時間のプログラムの実行結果、あるいはプログラム実行に起因する（WEB表示等の）状態が予想と一致するか、テストしたい。
 * このテストは予め設定した値を元に、自動実行可能なものとする。

## プロトタイプ実装とデモ
 
 * プロトタイプとそれを使ったデモを作ってみました。かなり適当ですが。
  * デモ：https://github.com/tomonsaurus/timetest_server
  * プロトタイプの実体： https://github.com/tomonsaurus/timetest_src
 * vagrantでテスト実行サーバー(admin)とテスト対象サーバー(web1, web2)を立ち上げてテストします。
   * 環境：windows10 vagrant1.9.6 virtualbox5.1.24 git2.9.0
 * 実行方法等はREADME.mdを。

### 概要

[テスト実行サーバー admin] 192.168.33.110 centos7
　↓↓ ・ansibleによってテスト対象サーバーの時間を変更(crond , atdを再起動)
　↓↓ ・時間を変更後、cronやWEBページの状態を取得
　↓↓
[テスト対象サーバー web1] 192.168.33.20 centos6
[テスト対象サーバー web2] 192.168.33.30 ubuntu
　 　* cron
　　 * atコマンド
　 　* 一定時間内に表示が変わるPHPのWEBページ
　 　* テスト実行サーバーから時間を変更される。

### デモ準備(vagrantで用意される環境)
 * テスト実行サーバー(admin)を用意。
   * 設定ファイルにテストしたい時間を記述。
   * テストしたい時間ごとに実行後に実行するテストプログラムを記述。
     * WEBトップページの表示内容をチェックするテスト
     * cronで書き出されるログをチェックするテスト
 * テスト対象サーバー(web1)にcronにプログラム設定。
 * テスト対象サーバー(web1,web2)一定時間帯に表示内容が変わるWEBページを用意。
 
 * テスト対象サーバー(web1,web2)はsudoでdate, ntpdateで時間設定可能。
   /etc/sudoers
 
  ```
    homepage    ALL=(root)    NOPASSWD: /bin/date, /sbin/ntpdate
  ```
### 注意
 * VirtualBoxではテスト対象サーバー(web1,web2)で、ホストとの自動時間同期をオフにする。
   *  http://www.virtualbox.org/manual/ch09.html#disabletimesync
   * トラブルシューティングも参照のこと。

### デモプログラムの動作

 * テスト実行サーバー(admin)で本テストプログラムを実行
   * 設定ファイルに記載した時間に、テスト対象サーバー(web1, web2)の時間をsudo date -s '****' で設定。ansible
   * テスト対象サーバー(web1, web2)で crond, atdを再起動させる。ansible
     * 複数サーバーへ、OS抽象化して実行するためansibleを利用。冪等性は不要なので、SpecInfraでもよさそう。
   
 * テスト対象サーバー(web1, web2)へその時間に実行を設定されているテストを実行。
   * デモでは、web1, web2へwebアクセスし、その時間に表示されるはずの文言をチェック。Infrataster
   * デモでは、web1へログ・ファイルにアクセスし、cronでその時間に実行されると記録されるはずの文字列をチェック。Infrataster
 
 * 設定ファイルに記載された時間がすべてなくなるまでこれを繰り返し。
 * 最後にテスト対象サーバー(web1, web2)でntpdateで現在日時をもとに戻しておく。ansible

### デモプログラムの動作　キャプチャー

 * プログラムスタート
![start.jpg](https://qiita-image-store.s3.amazonaws.com/0/108908/9be1aa1c-4daf-0056-5b1b-ae61dabb2343.jpeg)

 * 時間設定
![set.jpg](https://qiita-image-store.s3.amazonaws.com/0/108908/4bcfdf9e-08f9-2bb3-4e37-ffcb23aaa870.jpeg)

 * テスト実行
![test.jpg](https://qiita-image-store.s3.amazonaws.com/0/108908/d386c6d7-3cc2-eade-a8c7-49bf2101d8ba.jpeg)

 * プログラム終了
![end.jpg](https://qiita-image-store.s3.amazonaws.com/0/108908/4ae96e00-25aa-a589-7d84-16ebc659e782.jpeg)



## ハマったところ、問題点とか
 * cronが言うことを聞いてくれない！！
    * cronに設定された、ある時間に実行されるプログラムがあったとして、その時間の５秒前くらいにdateコマンドでサーバー時間を設定。
      しかし、５秒後にはcron設定されたプログラムは実行されません。最低１分以上前に設定する必要があります。
      これはcronの仕様によるためのようです。

    * 参考：
       cron の意外な落とし穴！
       http://moro-archive.hatenablog.com/entry/2015/03/17/011823

     * かなり適当に、関係するところだけを要約すると最低1分前から時間設定がされていないとcronに設定されたプログラムは実行されないという感じです。 

     * これは結構きついです。テスト項目が２０個あれば２０分以上はかかるということです。
  
     * そこでcronについては、cronで実行せずに、プログラム側でcron設定を読み取り、該当時間ならそのプログラムを実行してやるという方法もあります。

     * ただこれだと厳密なテストにはなりません。
 例えばcron設定で問題になる環境変数の違いによる挙動の違いがチェックできません。
         * ※cronから実行されたときの環境変数と、直接叩いたときの環境変数が異なる問題。結構はまることがあるようです。
         参考URL: http://qiita.com/nog/items/0e9ab97cf59d82cdf6be
 
    * プロトタイプでの解決方法
      * いろいろ試した結果、時間を新たに設定したあとcrondを再起動させる(service crond restart)方法をとりました。プロトタイプではこうしています。(ansibleでOS抽象化したので他のOSでもいけるはず)
   
 * 厳密なその時間の状態を再現するのは難しい。

  * サーバー時間を変更してテストしますが、結局とびとびの時間になってしまうので
  本番サーバー稼働時とは厳密に同じにすることは難しいですね。

 * 問題点はその他諸々ありますが割愛。
   * こういうテストツールがありそうでないのはサーバーごと時間制御するのが難しく、厳密なテストをするのが難しいからなんだろうなと思いました。 
   
## 先行する類似の機能とかツールとかモジュールとか
  
 * RailsでTimecopを使って日付/時間のテストをする

    http://ruby-rails.hatenadiary.com/entry/20141218/1418901424
    目的等が同じですが、サーバーまるごとではなく、Ruby on Rails 上のみ時間をあやつるもののようです。
同様のPerl,PHPのツールもあるようです。

## 最後に

 * このテストフレームワークが役に立つシステムや状況は割りと限られていそう。
   自分が扱っているシステムで役立つので作りましたが。。以下の場合には使えると思います。
   * cron設定の数が多く、時々設定が変わる。
   * 時間によって表示内容が変わるWEBページがあり、その時間設定がよく変わる。

 * プロトタイプで大体のところはできるので、私の用途にはこれで十分ですが、きっちりやろうとするといろんな問題がいっぱいあります。
   * 例えば
     * テストしたい時間の数秒前に時間設定してservice crond restartしますがその数秒の間にcrond再起動が完了しない場合はどうするのか。
     * 時間設定後、service crond restart後、sleepを数秒いれて結果を取得しますがcron設定されたプログラムがsleepの数秒間に完了するのか、とか。
     * cron以外の時間で実行するプログラムの対応　参照：http://qiita.com/shrkw/items/5c3d53358b0016a09504
 * まあ、今回はこんなところで。。