2017-08-21 14 views
0

exec.Command()を使用するユニットテスト機能を覚えました。すなわち、exec.Command()を嘲笑します。私は、より多くのユニットケースを追加して行きましたが、さまざまなシナリオで出力をモックできないという問題にぶつかっていました。ここでGo langでexec.Commandを複数の単体テストでモックする方法は?

以下

package main 

import (
    "fmt" 
    "os/exec" 
) 

var execCommand = exec.Command 

func printDate() ([]byte, error) { 
    cmd := execCommand("date") 
    out, err := cmd.CombinedOutput() 
    return out, err 
} 

func main() { 
    fmt.Printf("hello, world\n") 
    fmt.Println(printDate()) 
} 

package main 

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

var mockedExitStatus = 1 
var mockedDate = "Sun Aug 20" 
var expDate = "Sun Aug 20" 

func fakeExecCommand(command string, args ...string) *exec.Cmd { 
    cs := []string{"-test.run=TestHelperProcess", "--", command} 
    cs = append(cs, args...) 
    cmd := exec.Command(os.Args[0], cs...) 
    cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} 
    return cmd 
} 

func TestHelperProcess(t *testing.T) { 
    if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { 
     return 
    } 

    // println("Mocked Data:", mockedDate) 
    fmt.Fprintf(os.Stdout, mockedDate) 
    os.Exit(mockedExitStatus) 
} 

func TestPrintDate(t *testing.T) { 
    execCommand = fakeExecCommand 
    defer func() { execCommand = exec.Command }() 

    out, err := printDate() 
    print("Std out: ", string(out)) 
    if err != nil { 
     t.Errorf("Expected nil error, got %#v", err) 
    } 
    if string(out) != expDate { 
     t.Errorf("Expected %q, got %q", expDate, string(out)) 
    } 
} 

func TestPrintDateUnableToRunError(t *testing.T) { 
    execCommand = fakeExecCommand 
    defer func() { execCommand = exec.Command }() 

    mockedExitStatus = 1 
    mockedDate = "Unable to run date command" 
    expDate = "Unable to run date command" 

    out, err := printDate() 
    print("Std out: ", string(out)) 
    if err != nil { 
     t.Errorf("Expected nil error, got %#v", err) 
    } 
    if string(out) != expDate { 
     t.Errorf("Expected %q, got %q", expDate, string(out)) 
    } 
} 

go testがために失敗した...テストコードhello_test.goです...私はテストしようとしているサンプルコードhello.goです第2の試験TestPrintDateUnableToRunError ...

$ go test hello 
Std out: Sun Aug 20Std out: Sun Aug 20--- FAIL: TestPrintDateTomorrow (0.01s) 
    hello_test.go:62: Expected "Unable to run date command", got "Sun Aug 20" 
FAIL 
FAIL hello 0.017s 

グローバルmockedDateの値をテストケース内に設定しようとしても、初期化されたグローバル値は引き続き取得されています。 グローバル値は設定されていませんか?または、グローバル変数への変更がTestHelperProcessに更新されていませんか?

答えて

0

私は、このためのソリューションを持って...

が設定され得ていないグローバル値はありますか?または、そのグローバルvarへの変更がTestHelperProcessで更新されていませんか?

TestPrintDate()で、 fakeExecCommandはなくexec.Commandで呼ばれているので

、とだけTestHelperProcess()を実行するためにfakeExecCommand実行go testを呼び出すには、それは完全にのみTestHelperProcess()が実行される新しい呼び出します。 TestHelperProcess()のみが呼び出されるため、グローバル変数は設定されていません。

解決策は、fakeExecCommandにEnvを設定し、それをTestHelperProcess()に取得し、それらの値を戻すことです。

PS>TestHelperProcessは、TestExecCommandHelperに名前が変更され、いくつかの変数の名前が変更されます。以下のように

package main 

import (
    "fmt" 
    "os" 
    "os/exec" 
    "strconv" 
    "testing" 
) 

var mockedExitStatus = 0 
var mockedStdout string 

func fakeExecCommand(command string, args ...string) *exec.Cmd { 
    cs := []string{"-test.run=TestExecCommandHelper", "--", command} 
    cs = append(cs, args...) 
    cmd := exec.Command(os.Args[0], cs...) 
    es := strconv.Itoa(mockedExitStatus) 
    cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1", 
     "STDOUT=" + mockedStdout, 
     "EXIT_STATUS=" + es} 
    return cmd 
} 

func TestExecCommandHelper(t *testing.T) { 
    if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { 
     return 
    } 

    // println("Mocked stdout:", os.Getenv("STDOUT")) 
    fmt.Fprintf(os.Stdout, os.Getenv("STDOUT")) 
    i, _ := strconv.Atoi(os.Getenv("EXIT_STATUS")) 
    os.Exit(i) 
} 

func TestPrintDate(t *testing.T) { 
    mockedExitStatus = 1 
    mockedStdout = "Sun Aug 201" 
    execCommand = fakeExecCommand 
    defer func() { execCommand = exec.Command }() 
    expDate := "Sun Aug 20" 

    out, _ := printDate() 
    if string(out) != expDate { 
     t.Errorf("Expected %q, got %q", expDate, string(out)) 
    } 
} 

func TestPrintDateUnableToRunError(t *testing.T) { 
    mockedExitStatus = 1 
    mockedStdout = "Unable to run date command" 
    execCommand = fakeExecCommand 
    defer func() { execCommand = exec.Command }() 

    expDate := "Unable to run date command" 

    out, _ := printDate() 
    // println("Stdout: ", string(out)) 
    if string(out) != expDate { 
     t.Errorf("Expected %q, got %q", expDate, string(out)) 
    } 
} 

go test結果... (モックが正常に動作していることを示すために1つのテストに失敗しています)。

go test hello 
--- FAIL: TestPrintDate (0.01s) 
     hello_test.go:45: Expected "Sun Aug 20", got "Sun Aug 201" 
FAIL 
FAIL hello 0.018s 
0

投稿したコードに基づいて、mockedDate変数は何もしません。テストもprintDate()への呼び出しもそれを利用していないので、TestPrintDateUnableToRunError()テストはそれ以前のテストと同じように実行されます。

printDate()関数に機能を追加して、 "実行不能コマンド"(この場合)の文字列を返すと、62行目の条件が成立します。つまり、返品値にprintDate()のエラーがある場合、そのようなチェックは不要です。返されるエラーがnil以外の場合、返される出力文字列は無効(または空の場合、"")であるはずです。

実際にprintDate()がどのように失敗しているかはわかりませんが、そのままでは、TestPrintDateUnableToRunError()に期待している値を返す方法はありません。

+0

...例として、私だけで使用される日付、ここで尋ねるために...おかげで私は実際にいくつかのスクリプトを呼び出ししようとしていた、そしてそのスクリプトは時々存在していない可能性があります。ところで 私は後でENV変数を利用してそれを行うことができることに気付きました。 – abhijithda

関連する問題