Masih melanjutkan dari modul sebelumnya, kita akan ubah program agar bisa menerima user input.
Buka file main.go, lalu ubah seperti berikut.
package main
import (
"bufio"
"fmt"
"io"
"os"
"strconv"
"strings"
)
func main() {
// welcome message
intro()
//channel untuk quit
doneChan := make(chan bool)
//jalankan go routine untuk membaca user input
go readUserInput(os.Stdin, doneChan)
//block sampai doneChan mendapatkan value
<-doneChan
//close channel
close(doneChan)
fmt.Println("Bye...")
}
func readUserInput(in io.Reader, doneChan chan bool) {
scanner := bufio.NewScanner(in)
for {
res, done := checkNumbers(scanner)
if done {
doneChan <- true
return
}
fmt.Println(res)
}
}
func checkNumbers(scanner *bufio.Scanner) (string, bool) {
//read user input
scanner.Scan()
//check user quit
if strings.EqualFold(scanner.Text(), "q") {
return "", true
}
numToCheck, err := strconv.Atoi(scanner.Text())
if err != nil {
return "Enter whole number", false
}
_, msg := isPrime(numToCheck)
return msg, false
}
func intro() {
fmt.Println("Prime Number")
fmt.Println("------------")
fmt.Println("Enter whole number (q for quit).")
myPrompt()
}
func myPrompt() {
fmt.Print("-> ")
}
func isPrime(n int) (bool, string) {
if n == 0 || n == 1 {
return false, fmt.Sprintf("%d is not prime", n)
}
if n < 0 {
return false, "negative is not prime"
}
for i := 2; i <= n/2; i++ {
if n%i == 0 {
return false, fmt.Sprintf("%d is not prime because is divisible by %d", n, i)
}
}
return true, fmt.Sprintf("%d is prime number", n)
}
Pertama kita akan membuat Test untuk fungsi prompt().
Buka file main_test.go, lalu tambahkan code dibawah (lihat baris komentar untuk penjelasan code).
func Test_prompt(t *testing.T) {
//save copy of os.Stdout
oldOut := os.Stdout
//create read and write pipe
r, w, _ := os.Pipe()
//set os.Stdout to write pipe
os.Stdout = w
myPrompt()
//close writer
_ = w.Close()
//reset os.Stdout
os.Stdout = oldOut
//read output dari myPrompt() func
out, _ := io.ReadAll(r)
//perform test
if string(out) != "-> " {
t.Errorf("Incorrect prompt: expected -> but got %s", string(out))
}
}
Selanjutnya adalah membuat test untuk fungsi intro(). Tambahkan code berikut.
func Test_prompt(t *testing.T) {
//save copy of os.Stdout
oldOut := os.Stdout
//create read and write pipe
r, w, _ := os.Pipe()
//set os.Stdout to write pipe
os.Stdout = w
myPrompt()
//close writer
_ = w.Close()
//reset os.Stdout
os.Stdout = oldOut
//read output dari myPrompt() func
out, _ := io.ReadAll(r)
//perform test
if string(out) != "-> " {
t.Errorf("Incorrect prompt: expected -> but got %s", string(out))
}
}
func Test_intro(t *testing.T) {
//save copy of os.Stdout
oldOut := os.Stdout
//create read and write pipe
r, w, _ := os.Pipe()
//set os.Stdout to write pipe
os.Stdout = w
intro()
//close writer
_ = w.Close()
//reset os.Stdout
os.Stdout = oldOut
//read output dari myPrompt() func
out, _ := io.ReadAll(r)
//perform test
if !strings.Contains(string(out), "Enter whole number") {
t.Errorf("Incorrect intro text, got %s", string(out))
}
}
Kemudian tambahkan test untuk fungsi checkNumbers().
func Test_checkNumbers(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{"empty", "", "Enter whole number"},
{"zero", "0", "0 is not prime"},
{"one", "1", "1 is not prime"},
{"two", "2", "2 is prime number"},
{"four", "4", "4 is not prime because is divisible by 2"},
{"negative", "-2", "negative is not prime"},
{"decimal", "4.0", "Enter whole number"},
{"quit", "q", ""},
}
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, but got %s", e.expected, res)
}
}
}
Fungsi terakhir yang perlu dibuat test adalah readUserInput(), gunakan kode berikut untuk fungsi testing Test_readUserInput().
func Test_readUserInput(t *testing.T) {
//untuk test fungsi ini, diperlukan channel dan io.Reader instance
doneChan := make(chan bool)
//reference ke bytes.Buffer
var stdin bytes.Buffer
stdin.Write([]byte("1\nq\n"))
go readUserInput(&stdin, doneChan)
<-doneChan
close(doneChan)
}
Berikut isi lengkap dari main_test.go
package main
import (
"bufio"
"bytes"
"io"
"os"
"strings"
"testing"
)
func Test_isPrime(t *testing.T) {
primeTests := []struct {
name string
testNum int
expected bool
msg string
}{
{"prime", 7, true, "7 is prime number"},
{"not prime", 4, false, "4 is not prime because is divisible by 2"},
{"zero", 0, false, "0 is not prime"},
{"one", 1, false, "1 is not prime"},
{"negatif", -1, false, "negative is not prime"},
}
for _, e := range primeTests {
result, msg := isPrime(e.testNum)
if e.expected && !result {
t.Errorf("%s: expected true but got false", e.name)
}
if !e.expected && result {
t.Errorf("%s: expected false but got true", e.name)
}
if e.msg != msg {
t.Errorf("%s: expected %s but got %s", e.name, e.msg, msg)
}
}
}
func Test_prompt(t *testing.T) {
//save copy of os.Stdout
oldOut := os.Stdout
//create read and write pipe
r, w, _ := os.Pipe()
//set os.Stdout to write pipe
os.Stdout = w
myPrompt()
//close writer
_ = w.Close()
//reset os.Stdout
os.Stdout = oldOut
//read output dari myPrompt() func
out, _ := io.ReadAll(r)
//perform test
if string(out) != "-> " {
t.Errorf("Incorrect prompt: expected -> but got %s", string(out))
}
}
func Test_intro(t *testing.T) {
//save copy of os.Stdout
oldOut := os.Stdout
//create read and write pipe
r, w, _ := os.Pipe()
//set os.Stdout to write pipe
os.Stdout = w
intro()
//close writer
_ = w.Close()
//reset os.Stdout
os.Stdout = oldOut
//read output dari myPrompt() func
out, _ := io.ReadAll(r)
//perform test
if !strings.Contains(string(out), "Enter whole number") {
t.Errorf("Incorrect intro text, got %s", string(out))
}
}
func Test_checkNumbers(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{"empty", "", "Enter whole number"},
{"zero", "0", "0 is not prime"},
{"one", "1", "1 is not prime"},
{"two", "2", "2 is prime number"},
{"four", "4", "4 is not prime because is divisible by 2"},
{"negative", "-2", "negative is not prime"},
{"decimal", "4.0", "Enter whole number"},
{"quit", "q", ""},
}
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, but got %s", e.expected, res)
}
}
}
func Test_readUserInput(t *testing.T) {
//untuk test fungsi ini, diperlukan channel dan io.Reader instance
doneChan := make(chan bool)
//reference ke bytes.Buffer
var stdin bytes.Buffer
stdin.Write([]byte("1\nq\n"))
go readUserInput(&stdin, doneChan)
<-doneChan
close(doneChan)
}
Jika Anda periksa menggunakan coverage, maka seluruh fungsi sudah berhasil ditest.
$ go test -cover .
ok primeapp (cached) coverage: 82.4% of statements
Kita bisa lihat juga report coverage profile
$ go test -coverprofile=coverage
$ go tool cover -html=coverage

Sampai disini kita sudah membahas membuat test untuk fungsi yang menggunakan input dan output.