#0. 테이블 드리븐 테스트에서 사용자 입력에 대한 테스트

사용자가 어떤 값을 입력한다고 가정하여 그 값과 예상되는 값과 일치하는지 비교합니다. 

 

#1. 실습

maim.go와 main_test.go로 테스트 실습.

package main

import (
    "bufio"
    "fmt"
    "os"
    "strconv"
    "strings"
)

func main() {
    // print a welcome message
    intro()
    // create a channel to indicate when the user wants to quit
    doneChan := make(chan bool)
    // start a goroutine to read user input and run program
    go readUserInput(doneChan)
    // block until the doneChan gets a value
    <-doneChan
    // close the channel
    close(doneChan)
    // say goodbye
    fmt.Println("Goodbye.")
}

func readUserInput(doneChan chan bool) {
    scanner := bufio.NewScanner(os.Stdin)
    for {
       res, done := checkNumbers(scanner)
       if done {
          doneChan <- true
          return
       }
       fmt.Println(res)
       prompt()
    }
}

func checkNumbers(scanner *bufio.Scanner) (string, bool) {
    // read user input
    scanner.Scan()
    //check to see if the user wants to quit
    if strings.EqualFold(scanner.Text(), "q") {
       return "", true
    }
    // try to convert what the user typed into an int
    numToCheck, err := strconv.Atoi(scanner.Text())
    if err != nil {
       return "Please enter a whole number", false
    }
    _, msg := isPrime(numToCheck)
    return msg, false
}
func intro() {
    fmt.Println("Is it Prime?")
    fmt.Println("------------")
    fmt.Println("Enter a whole number, and we'll tell you if it is a prime number or not. Enter q to quit.")
    prompt()
}
func prompt() {
    fmt.Print("-> ")
}
func isPrime(n int) (bool, string) {
    // 0과 1 처리(0과 1은 소수가 아님)
    if n == 0 || n == 1 {
       return false, fmt.Sprintf("%d is not prime, by definition!", n)
    }
    // 0보다 작은 수의 경우
    if n < 0 {
       return false, "Negative numbers are not prime, by definition!"
    }
    // 소수인지 아닌지 체크
    for i := 2; i <= n/2; i++ {
       if n%i == 0 {
          // 소수가 아닌 경우
          return false, fmt.Sprintf("%d is not a prime number because it is divisible by %d!", n, i)
       }
    }
    //소수인 경우
    return true, fmt.Sprintf("%d is a prime number!", n)
}

1. main.go 파일

소수인지 체크하는 프로그램으로써. 소수인 경우, 소수가 아닌 경우, 0과1인 경우, 0보다작은 경우(-1)를 체크한다.

그리고 사용자에게 입력을 받고 문자열을 출력하는 함수(intro(), prompt(), checkNumbers(), readUserInput())들을 추가했다.

 

package main

import (
	"bufio"
	"io"
	"os"
	"strings"
	"testing"
)

func Test_isPrime(t *testing.T) {
	primeTests := []struct {
		name     string
		testNum  int    //테스트 할 값
		expected bool   //예상되는 bool값
		msg      string //예상되는 메시지
	}{
		{"prime", 7, true, "7 is a prime number!"},
		{"not prime", 8, false, "8 is not a prime number because it is divisible by 2!"},
		{"zero", 0, false, "0 is not prime, by definition!"},
		{"one", 1, false, "1 is not prime, by definition!"},
		{"negative number", -11, false, "Negative numbers are not prime, by definition!"},
	}

	for _, e := range primeTests {
		result, msg := isPrime(e.testNum)

		//true가 예상되지만 false를 반환한 경우
		if e.expected && !result {
			t.Errorf("%s: expected true but got false", e.name)
		}
		//false가 예상되지만 true를 반환한 경우
		if !e.expected && result {
			t.Errorf("%s: expected false but got true", e.name)
		}

		//예상되는 e.msg값과 반환한 msg값이 다른 경우
		if e.msg != msg {
			t.Errorf("%s: expected %s but got %s", e.name, e.msg, msg)
		}
	}
}

func Test_prompt(t *testing.T) {
	// save a copy of os.Stdout
	oldOut := os.Stdout

	// create a read and write pipe
	r, w, _ := os.Pipe()

	// set os.Stdout to our write pipe
	os.Stdout = w

	prompt()

	// close our write
	_ = w.Close()

	// reset os.Stdout to what it was before
	os.Stdout = oldOut

	// read the output of our prompt() func from our read pipe
	out, _ := io.ReadAll(r)

	// perform our test
	if string(out) != "-> " {
		t.Errorf("incorrect prompt: expected -> but got %s", string(out))
	}
}

func Test_intro(t *testing.T) {
	// save a copy of os.Stdout
	oldOut := os.Stdout

	// create a read and write pipe
	r, w, _ := os.Pipe()

	// set os.Stdout to our write pipe
	os.Stdout = w

	intro()

	// close our write
	_ = w.Close()

	// reset os.Stdout to what it was before
	os.Stdout = oldOut

	// read the output of our prompt() func from our read pipe
	out, _ := io.ReadAll(r)

	if !strings.Contains(string(out), "Enter a whole number") {
		t.Errorf("intro text not correct; got %s", string(out))
	}
}

func Test_checkNumbers(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{name: "empty", input: "", expected: "Please enter a whole number!"},
		{name: "zero", input: "0", expected: "0 is not prime, by definition!"},
		{name: "one", input: "1", expected: "1 is not prime, by definition!"},
		{name: "two", input: "2", expected: "2 is a prime number!"},
		{name: "three", input: "3", expected: "3 is a prime number!"},
		{name: "negative", input: "-1", expected: "Negative numbers are not prime, by definition!"},
		{name: "typed", input: "three", expected: "Please enter a whole number!"},
		{name: "decimal", input: "1.1", expected: "Please enter a whole number!"},
		{name: "quit", input: "q", expected: ""},
		{name: "QUIT", input: "Q", expected: ""},
	}
	for _, e := range tests {
		input := strings.NewReader(e.input)
		reader := bufio.NewScanner(input)
		res, _ := checkNumbers(reader)

		if !strings.EqualFold(res, e.expected) {
			t.Errorf("%s: expected %s, but got %s", e.name, e.expected, res)
		}
	}
}

2. main_test.go 파일 (func Test_checkNumbers)

 1. tests에 테이블 테스트를 위한 구조체 슬라이스 저장.

 2. name(값 이름),  input(사용자가 입력을 했다고 가정), expected(사용자 입력(input)에 따른 예상되는 메세지)

 3. 구조체 슬라이스로 만든 테이블 테스트 슬라이스를 반복문으로 돌면서 확인

 4. input := strings.NewReader(e.input) : e.input에서 문자열을 가져와서 새로운 Reader를 만들어줌.

 5. reader := bufio.NewScanner(input) : input에서 값을 가져와서 새로운 Scanner를 만들어줌.

 6. res, _ := checkNumber(reader) : checkNumbers 함수에 reader를 전달하고 반환 값을 받습니다.

 7. if !strings.EqualFold(res, e.expected) : checkNumber 함수에서 반환한 값과 예상되는 메세지를 비교합니다.

 8. 비교해서 메세지가 똑같으면 성공.

 

#2. 결과

테스트 통과

 

1. go test -v, go test -cover 명령어로 테스트 범위와 통과 확인.

 

html 파일로 테스트 cover 확인

html 파일로 테스트 cover 확인

2. go test . -coverprofile=coverage.out 명령어로 coverage.out 파일을 만들어줌
3. go tool cover -html=coverage.out(윈도우에서는 .out없이)  명령어로 html 파일로 확인. (초록색 부분이 cover(테스트 코드 작성 완료))

 

출처
https://www.udemy.com/course/introduction-to-testing-in-go-golang/

+ Recent posts