OS実行
OS Exec
keith6014redditで質問する
XMLデータを生成したos/exec.Command()を使用してコマンドを実行しています。コマンドは、GetData()という関数で実行されます。
GetData()をテストするために、作成したtestdataをいくつか持っています。
私の_test.goには、GetData()を呼び出すTestGetDataがありますが、os.execを使用しますが、代わりにtestdataを使用します。
これを達成するための良い方法は何ですか? GetDataを呼び出すときは、「テスト」フラグモードを使用して、GetData(mode string)というファイルを読み取る必要がありますか?
いくつかのこと
  • 何かをテストするのが難しい場合、懸念の分離が正しくないことが原因であることがよくあります
  • コードに「テストモード」を追加しないでください。代わりに、依存関係の注入(DI)を使用して、依存関係をモデル化し、懸念事項を分離できるようにします。
私は勝手にコードがどのように見えるかを推測しました。
1
type Payload struct {
2
Message string `xml:"message"`
3
}
4
5
func GetData() string {
6
cmd := exec.Command("cat", "msg.xml")
7
8
out, _ := cmd.StdoutPipe()
9
var payload Payload
10
decoder := xml.NewDecoder(out)
11
12
// these 3 can return errors but I'm ignoring for brevity
13
cmd.Start()
14
decoder.Decode(&payload)
15
cmd.Wait()
16
17
return strings.ToUpper(payload.Message)
18
}
Copied!
  • プロセスへの外部コマンドを実行できるexec.Commandを使用します
  • 出力をcmd.StdoutPipeにキャプチャして、io.ReadCloserを返します(これは重要になります)
  • コードの残りの部分は、優れたドキュメントから多かれ少なかれコピーして貼り付けたものです。
    • stdoutからの出力をキャプチャしてio.ReadCloserに取り込み、次にコマンドをStartしてから、Waitを呼び出してすべてのデータが読み取られるのを待ちます。これらの2つの呼び出しの間で、データをPayload構造体にデコードします。
これが msg.xml内に含まれているものです。
1
<payload>
2
<message>Happy New Year!</message>
3
</payload>
Copied!
実際の動作を示す簡単なテストを作成しました。
1
func TestGetData(t *testing.T) {
2
got := GetData()
3
want := "HAPPY NEW YEAR!"
4
5
if got != want {
6
t.Errorf("got %q, want %q", got, want)
7
}
8
}
Copied!

テスト可能なコード

テスト可能なコードは分離され、単一の目的です。 私には、このコードには2つの主な懸念があるように感じます
  1. 1.
    未加工のXMLデータを取得する。
  2. 2.
    XMLデータをデコードし、ビジネスロジックを適用します(この場合は<message>strings.ToUpperです)
最初の部分は、サンプルを標準libからコピーすることです。
2番目の部分はビジネスロジックがある場所です。コードを見ると、ロジックの「シーム"seam"」がどこから始まるかがわかります。ここで、io.ReadCloserを取得します。この既存の抽象化を使用して、問題を分離し、コードをテスト可能にすることができます。
GetDataの問題は、ビジネスロジックがXMLを取得する手段と結合していることです。デザインを改善するには、それらを切り離す必要があります
私たちのTestGetDataは2つの懸念事項の間の統合テストとして機能することができるため、それが機能し続けることを確認するためにそれを保持します。
新しく分離されたコードは次のようになります
1
type Payload struct {
2
Message string `xml:"message"`
3
}
4
5
func GetData(data io.Reader) string {
6
var payload Payload
7
xml.NewDecoder(data).Decode(&payload)
8
return strings.ToUpper(payload.Message)
9
}
10
11
func getXMLFromCommand() io.Reader {
12
cmd := exec.Command("cat", "msg.xml")
13
out, _ := cmd.StdoutPipe()
14
15
cmd.Start()
16
data, _ := ioutil.ReadAll(out)
17
cmd.Wait()
18
19
return bytes.NewReader(data)
20
}
21
22
func TestGetDataIntegration(t *testing.T) {
23
got := GetData(getXMLFromCommand())
24
want := "HAPPY NEW YEAR!"
25
26
if got != want {
27
t.Errorf("got %q, want %q", got, want)
28
}
29
}
Copied!
GetDataが入力をio.Readerから取得するようになったので、これをテスト可能にして、データの取得方法を考慮しなくなりました。人々はio.Reader(これは非常に一般的です)を返すもので関数を再利用できます。
たとえば、コマンドラインではなくURLからXMLのフェッチを開始できます。
1
func TestGetData(t *testing.T) {
2
input := strings.NewReader(`
3
<payload>
4
<message>Cats are the best animal</message>
5
</payload>`)
6
7
got := GetData(input)
8
want := "CATS ARE THE BEST ANIMAL"
9
10
if got != want {
11
t.Errorf("got %q, want %q", got, want)
12
}
13
}
Copied!
これはGetDataの単体テストの例です。
懸念を分離し、Goのテストで既存の抽象化を使用することで、重要なビジネスロジックが簡単になります。
最終更新 1yr ago
リンクのコピー