write ahead log

ロールフォワード用

golangでhttptestを使ってテストする

ちょっとしたコマンドとか作る時にWebAPIを触ろうとすると,
net/httpをよく使うことになる.

テストについてはちょっと調べたんだけどhttp経由のものはやったことがなかった.
net/httpはテストについてもnet/http/httptestがサポートしてくれているのでこれを使うことにする.

余談だけど, 標準でなんでも入っているってのは本当に楽でいい.
ライブラリをあれこれ探したりランタイムを入れて環境構築するのにはもう疲れてきた.
本質からズレているような気がしてくる.

簡単なサンプルは以下の通り. GETリクエストで挨拶をするだけの関数がある.

[sample.go]

package main

import (
    "fmt"
    "net/http"
    "io/ioutil"
)

func remoteHello(domain string) string {
    // /greetingにクエリパラメータgreet=Helloを渡してGet問い合わせする
    res, err := http.Get(domain + "/greeting?greet=Hello")

    // エラー処理
    if err != nil {
        fmt.Println("Error")
        return "error"
    }
    defer res.Body.Close()

    // レスポンスを戻り値にする
    res_str, _ := ioutil.ReadAll(res.Body)
    return string(res_str)
}

これをテストするためのコードが以下の通り.

package main

import (
    "testing"
    "net/http/httptest"
    "net/http"
    "fmt"
)

func TestRemoteHello(t *testing.T) {
    // テストサーバを用意する
    // サーバ側でアクセスする側のテストを行う
    ts := httptest.NewServer(http.HandlerFunc(
        func(w http.ResponseWriter, r *http.Request) {
            // URLのアクセスパスが誤っていないかチェック
            if r.URL.Path != "/greeting" {
                t.Fatalf("誤ったアクセスパスでアクセス!")
            }
            // クエリパラメータをチェック
            if r.URL.Query().Get("greet") != "Hello" {
                t.Fatalf("正しく挨拶してない!")
            }
            // レスポンスを設定する
            w.Header().Set("content-Type", "text")
            fmt.Fprintf(w, "world")
            return
        },
    ))
    defer ts.Close()


    // クライアントのコードを呼び出す.
    // アクセスされる側(サーバ)のレスポンスのテストを行う.

    // テストサーバのルートパスはts.URLで取得できます
    res := remoteHello(ts.URL)

    if res != "world" {
        t.Errorf("世界じゃなかった.")
    }
}

ポイントは

  • httptest.NewServerでテストサーバを立てる
  • テストサーバのURLメソッドでテストサーバへのルートパスを手に入れられる

の2つだろうか.

もっと色々できる気もするが,とりあえずこれでダミーのリクエストとレスポンスが作れるので 必要十分かなと.

GitHubのReleaseのダウンロード数を調べるコマンドを作った

別にcurl叩けばわかるんだけど,面白そうだし作った.

github.com

使い方

  1. ここからバイナリをダウンロードしてください.
  2. GitHubにアクセスして, Home -> Setting -> Personal access tokens -> Generate new tokenからアクセストークンを取得してください.
  3. 環境変数を設定してください(下記).
  4. コマンドを実行してみてください(下記).
環境変数
変数名 内容
GDC_ID GitHubのAccount ID
GDC_ACCESS_TOKEN GitHubのAPI Access Token
コマンド

gdc [DL数を確認したいあなたのリポジトリ名]

twinbird@:~$ gdc hbp
[Name]: hbp
[Total]: 2
**************************************
[Tag Name]: 0.1
[Release Name]: 0.1
[Total Downloads]: 2
--------------------------------------
[Name]: darwin_386.zip
[Download Count]: 0
[Name]: darwin_amd64.zip
[Download Count]: 0
[Name]: linux_386.zip
[Download Count]: 0
[Name]: linux_amd64.zip
[Download Count]: 1
[Name]: windows_386.zip
[Download Count]: 0
[Name]: windows_amd64.zip
[Download Count]: 1

0じゃなかった.
CUIでブログ投稿したい人, 他にもいるのかね.


感想とか、どうでもいいこと

Dot Lang Viewerを作った時に気づいた事で,Chromeアプリは現在のユーザ数が表示されているということがある.

これが中々ありがたくて,数人でもユーザがいるなら作ってよかったなぁと思うものだったりする.
(聖人ではないので承認欲求くらいあります)

hbpを作った時にふと,GitHubに置いたもののダウンロード数もわかればなぁと思ったらAPIがあると知って試してみたくなって作った.

英語はやっぱ苦手.つかcountというよりsumaryな気がする.

golangは小さいコマンド作るのには重宝する.

なんと言ってもLinuxでもwindowsでもシングルバイナリで動くってのはでかい.

golangで色々なOSのバイナリを作る

前も調べたけど記録してなかったので忘れた.今度はメモっとく.

golangで他のプラットフォーム向けにクロスコンパイルするには環境変数を指定してbuildすれば良いらしい.

環境変数は

  • GOOS
  • GOARCH

があってそれぞれOSとCPUアーキテクチャを表している.

Linux環境でもwindowsの64bitバイナリを作るときには

GOOS=windows GOARCH=amd64 go build

とすれば良い.

選択できるOSとアーキテクチャについては公式に記載がある.

2016/4/17現在では

 $GOOS   $GOARCH
    darwin  386
    darwin  amd64
    darwin  arm
    darwin  arm64
    dragonfly   amd64
    freebsd 386
    freebsd amd64
    freebsd arm
    linux   386
    linux   amd64
    linux   arm
    linux   arm64
    linux   ppc64
    linux   ppc64le
    linux   mips64
    linux   mips64le
    netbsd  386
    netbsd  amd64
    netbsd  arm
    openbsd 386
    openbsd amd64
    openbsd arm
    plan9   386
    plan9   amd64
    solaris amd64
    windows 386
    windows amd64

こんな感じ.
もう十分じゃない?ってくらいある.素晴らしい.

毎回win, linux, macの3つ向けにビルドするのが面倒なので素朴なスクリプトを書いた.
あまり凝ってはいないけど,どこでも動くと思うし誰でも手を入れれると思う.

gist9cb8979e163e89ae6a88fd650291fd12

実行結果は以下.
サンプルとしてgdcをビルドした

twinbird@:~/Dropbox/go/src/gdc$ ./make.sh gdc
twinbird@:~/Dropbox/go/src/gdc$ ls
LICENSE    bin               github_access_test.go  make.sh
README.md  github_access.go  main.go
twinbird@:~/Dropbox/go/src/gdc$ tree bin/
bin/
├── darwin386
│   └── gdc
├── darwin64
│   └── gdc
├── linux386
│   └── gdc
├── linux64
│   └── gdc
├── windows386
│   └── gdc.exe
└── windows64
    └── gdc.exe

    6 directories, 6 files

golangではてなブログに投稿するCUIクライアントを作った

はてなブログにコマンドラインから投稿できるクライアントアプリを作りました. golangで作ったので多分どこでも動きます.
というか動かないと困るなぁ.

この記事もviでMarkdownで書いて以下のコマンドで投稿してます.
(プレビューと公開は子供に引きずられながらiPhoneからになると思いますが)

$ hbp -f blog.txt -c "go"

認証にはHTTPS + ベーシック認証使ってます.

使い方

使い方はこちらでREADME読んでください.

または

hbp -h

でも一応ヘルプが出ます.

はてなブログのAtomPub API使ってるので管理画面のAPI keyなんかが必要です.

はてなブログ管理画面->設定->詳細設定->AtomPub->APIキー を使ってください.

ダウンロード

ここにおいておきます.

駄文

goは初めて使ったので正直よくわかってない.

けどできるだけシンプルに抑えるところとか, 雰囲気Cに似てる(開発者を考えるとそれはそうなんだけど)のでとりあえず書くことは出来た.

メソッドの定義方法をさっき子供を寝かしつけながらiPhoneで知ったのでもっとやりようがあったかなぁと思う.

あとテストの書き方は調べたけど手抜きして書いてないのでもうちょっと調べて使いたい.

A Tour of Goをななめ読みして(しかも全部読んでない...)Googleに聞くというスタイルで一週間程度で完成.
(そういえばQiitaさんとそこへの投稿者の方には頭が上がりませんね)

言語学習から初めて正味12時間とかだと思うので言語のシンプルさが効いてる感じがする.

最初の数時間はPascal風味(?)な感じに抵抗があったんだけど,今はとても気に入っているので長く使いそうな言語です.

参考

公式ドキュメント

A Tour of Go

Hatena Developer Center

Qiita - Cybozu ガルーン API のレスポンスのXMLを golang でパースする

Qiita - Goでhttpリクエストを送信する方法

Qiita - Go のクロスコンパイル環境構築

golangで最低限のテストを書きたい

golangは触り始めたばかりで右も左もわからない状況なんだけれど,テストの書き方ぐらいは知っておこうかなと思う.

とはいえあんまり頑張ると途方もないので最低限に絞る.

テストされる側のプログラム
package main

func english() string {
    return "Hello, world"
}

func japanese() string {
    return "Hello, world"      /* <- ここがバグ */
    return "こんにちは、世界"
}

func chinese() string {
    return "你好,是世界"
}
テストする側のプログラム
package main

import (
    "testing"
)

func TestGreeting(t *testing.T) {
    if japanese() != "こんにちは、世界" {
        t.Errorf("Excepted %s, But %s.", "こんにちは、世界", japanese())
    }
    if chinese() != "你好,是世界" {
        t.Errorf("Excepted %s, But %s.", "你好,是世界", chinese())
    }
    if english() != "Hello, world" {
        t.Errorf("Excepted %s, But %s.", "Hello, world", english())
    }
}
テストに使うコマンド(標準で入ってる)
$ go test
--- FAIL: TestGreeting (0.00s)
        greeting_test.go:9: Excepted こんにちは、世界, But Hello, world.
FAIL
exit status 1
FAIL    _/C_/msys64/home/SASNUC_2/git/lab/gotest        0.140s
使いそうなメソッド
メソッド 説明
t.Logf 実行時の値を表示する
t.Errorf エラーメッセージを出す.テストの実行は止めない
t.Fatalf エラーメッセージを出す.テストの実行も止める

これだけあればとりあえずそれなりに書けるかと.

テストに対する考え方

公式に少し載っていました.

FAQ :テスト用のヘルパー関数がみあたりません
FAQ :アサート(assert)がない理由は?

個人的には肌に合う感じがします.
確認が必要で人手で行う必要のないものを自動化するのが自動テストの役割のはずですから.

正直テストは簡単なものならシェルスクリプトとかでも良いと思っている派なので.

複雑な事はしたくないというのが本音です.

iconvコマンドを使う[POSIXコマンド]

iconvコマンドは文字コード変換コマンドです. 名前は「International Codeset Conversion Library」に由来するそうです.
出典はwikipediaより

使い方

基本的にはfオプションとtオプションで入力元ファイルと出力先ファイルの円コーディングを指定して利用します.

例: CP932でエンコーディングされたファイルをUTF-8に変換する.
(やりがちですが, 同じファイル名を指定するとうまくいかないので注意が必要)

iconv -f CP932 -t UTF-8 [変換元ファイル名] > [変換先ファイル名] 

オプション

オプション 説明
-c 変換できない文字を破棄します
-f 入力の文字コードを指定します
-t 出力の文字コードを指定します
-l サポートする文字コードの一覧を表示します
-s 変換時に問題が発生した際にエラーメッセージを抑止します

cオプションを使えば変換できなかったときにも破棄して進むので無理にでもなんとかしたい場合は役に立ちます.

また,指定できる文字コードはしょっちゅう忘れるので(UTF-8なのかUTF8なのかとか)lオプションは個人的によく使います.