腰に優しいIoTテスト 〜 Akerunつくったエンジニアのそこそこ大規模なIoTプログラミング 〜

こんにちは。Akerunエンジニアの @ishturk です。

Akerun Advent Calendar13日目の記事です。

弊社フォトシンスは、ご存知のようにAkerunProKitというIoTサービスを提供しています。

昨年はラズパイを使った腰に優しい開発方法を紹介しました。

akerun.hateblo.jp

今回はさらに踏み込んで自動テストまで組み込んだ話です。

構成

  • akerun web APIs
  • AkerunProの動作ログをモニタリングする治具
  • ラズパイ
    • Logger
    • テストランナー

f:id:photosynth-inc:20171222175050p:plain

akerun web APIをコールするとAkerun Remote を経由して、指示がAkerun Proに到達して処理を実行します。 Loggerは起動後テキストでファイルに吐き出し続ける構成にしています。 テストランナーでAPIをコールし、Logに期待する結果するまで待つという構成です。 テストランナーはGoを使って書いてみました。

コード

package main

import (
    "fmt"
    "strings"
    "os/exec"
    "strconv"
     "encoding/json"
     "io/ioutil"
     "log"
     "net/http"
     "net/url"
)

func isWait(current int) bool {
    cmdstr := "wc -l autotest_log.log | cut -d \" \" -f 1"
    out, err := exec.Command("sh", "-c", cmdstr).Output()
    _ = err;
    s := string(out)
    s = strings.TrimRight(s, "\n")
    filelen, err := strconv.Atoi(s)
    if (current > filelen) {
        return true
    } else {
        return false
    }
}

func callAPI() {
     values := url.Values{}
     values.Add("param", "hogehoge")
     client := &http.Client{}
     req, err := http.NewRequest("POST", "https://あけるんAPI/hoge/hogera", nil)
     if err != nil {
          log.Fatal(err)
     }
     req.Header.Add("Authorization", "Bearer hogehogehogehogehogehogehogehogehogehogehogehogehogehogehoge")
     req.URL.RawQuery = values.Encode()
     res, err := client.Do(req)
     if err != nil {
          log.Fatal(err)
     }
     defer res.Body.Close()
     if res.StatusCode != http.StatusOK {
          log.Fatal(res)
     }
     body, err := ioutil.ReadAll(res.Body)
     if err != nil {
          log.Fatal(err)
     }
}

func isContain(line int, interval int, str string) bool {
    cmdstr := "sed " + strconv.Itoa(line) + "," + strconv.Itoa(line + interval) + "p autotest_log.log"
    out, err := exec.Command("sh", "-c", cmdstr).Output()
    _ = err;
    if (strings.Contains(string(out), str)) {
        return true
    } else {
        return false
    }
}

func main() {
    line := 0
    interval := 20
    timeout := 6000
    count := 0
    for count < timeout {
        if (isWait(line)) {
            continue
        }
        if (isContain(line, interval, "期待するログ")) {
            break;
        }
        count++
        line += interval
    }
    if (cout < timeout) {
        fmt.Println("PASS")
    else {
        fmt.Println("FAIL")
    }
}

APIは認証ヘッダ、パラメータ付与などしてPOSTしてます。もちろんいろいろできます。ggったらいろいろできるのでお試しあれ。 また、ログファイルの監視は sed コマンドを使って順番にキャプチャする方法で実現しました。無理してます。もっといい方法がある気がします。

おわりに

今回は APIを使ってE2Eテストを書いています。APIさえあればいろんなテストバリエーションを自動化できるのでいいですね。 エンジニアが拘束される作業時間が短くなるのでとても腰に優しいです。

また、Goは初めてコードを書いたのですが、とても書きやすいですね。組み込み畑のエンジニアがAPIをカジュアルに叩きたい場合は、オススメです。

告知

弊社ではエンジニアを募集しています!

www.wantedly.com