テストを書いてみよう
テストを書く前に、テスト対象になる処理が必要です。今回は、「与えられた City のリストから国ごとの人口の和」を計算する処理を書いてみます。
ヒント
- 国ごとにデータを分けて持つには
map
を使えばいいでしょう - 国単位で集計するので map の key は
CountryCode
を使うといいでしょう - データが入っていない場合もあるので、条件分岐には気を付けてください
参考実装
go
func calculatePopulation(cities []City) map[string]int64 {
result := make(map[string]int64)
for _, city := range cities {
if city.CountryCode.Valid {
// まだmapに存在しなかった場合、初期化する
if _, ok := result[city.CountryCode.String]; !ok {
result[city.CountryCode.String] = 0
}
result[city.CountryCode.String] += city.Population.Int64
}
}
return result
}
そうしたら、このコードが期待した値を返すかテストを書いてみましょう。
まず、calculate_test.go
を作成します。
TIP
Go では、_test
がファイル名の後ろについているファイルはテストファイルとして認識されます。
続いて、calculate_test.go
にテスト関数を実装していきます。
go
package main
import "testing"
// Testで始まる関数はテスト関数として認識されます
// testingはGoのテストのための標準ライブラリです
func Test_calculatePopulation(t *testing.T) {
// ここにテストを書いていく
}
まずは、空のリストを渡したときに、空のマップが返ってくることをテストしてみましょう。
go
package main
import "testing"
func Test_calculatePopulation(t *testing.T) {
// ここにテストを書いていく
cities := []City{}
got := calculatePopulation(cities)
want := map[string]int{}
// 長さが0になっているかどうかを確認する
if len(got) != 0 {
t.Errorf("calculatePopulation(%v) = %v, want %v", cities, got, want)
}
}
書き終わったら、関数の左上にあるrun test
か、そのさらに左にある再生ボタンを押して、テストを実行してみましょう。
すると、VSCode の Output にテストの結果が表示されます。
=== RUN Test_calculatePopulation
--- PASS: Test_calculatePopulation (0.00s)
PASS
ok test 0.001s
テストが正常に終了したことがわかりますね。
様々なケースをテストしてみよう
次に、calculatePopulation
のテストをもう少し充実させてみましょう。
これから複数のテストを書くため、先ほどのテストの関数名を変更します。
go
func Test_calculatePopulation_empty(t *testing.T) {
// ここにテストを書いていく
cities := []City{}
got := calculatePopulation(cities)
want := map[string]int{}
// 長さが0になっているかどうかを確認する
if len(got) != 0 {
t.Errorf("calculatePopulation(%v) = %v, want %v", cities, got, want)
}
}
課題
次のテストを実装してください。
- 1 つの国のみのデータが入っている場合
- 複数の国のデータが入っている場合
- 空のデータ(
city.CountryCode.Valid = false
)が入っている場合
答え
1 つの国のみのデータが入っている場合
go
// 1 つの国のみのデータが入っている場合
func Test_calculatePopulation_single(t *testing.T) {
cities := []City{
{
CountryCode: sql.NullString{
String: "JPN",
Valid: true,
},
Population: sql.NullInt64{
Int64: 100,
Valid: true,
},
},
{
CountryCode: sql.NullString{
String: "JPN",
Valid: true,
},
Population: sql.NullInt64{
Int64: 200,
Valid: true,
},
},
}
got := calculatePopulation(cities)
want := map[string]int64{
"JPN": 300,
}
// 長さが0になっているかどうかを確認する
if len(got) != len(want) {
t.Errorf("calculatePopulation(%v) = %v, want %v", cities, got, want)
}
// JPNの人口が100になっているかどうかを確認する
if got["JPN"] != want["JPN"] {
t.Errorf("calculatePopulation(%v) = %v, want %v", cities, got, want)
}
}
複数の国のデータが入っている場合
go
// 複数の国のデータが入っている場合
func Test_calculatePopulation_multiple(t *testing.T) {
cities := []City{
{
CountryCode: sql.NullString{
String: "JPN",
Valid: true,
},
Population: sql.NullInt64{
Int64: 100,
Valid: true,
},
},
{
CountryCode: sql.NullString{
String: "JPN",
Valid: true,
},
Population: sql.NullInt64{
Int64: 200,
Valid: true,
},
},
{
CountryCode: sql.NullString{
String: "USA",
Valid: true,
},
Population: sql.NullInt64{
Int64: 300,
Valid: true,
},
},
}
got := calculatePopulation(cities)
want := map[string]int64{
"JPN": 300,
"USA": 300,
}
// 長さが0になっているかどうかを確認する
if len(got) != len(want) {
t.Errorf("calculatePopulation(%v) = %v, want %v", cities, got, want)
}
for k, v := range got {
// 国ごとの人口が一致しているかどうかを確認する
if v != want[k] {
t.Errorf("calculatePopulation(%v) = %v, want %v", cities, got, want)
}
}
}
空のデータ(city.CountryCode.Valid = false
)が入っている場合
go
// 空のデータ(`city.CountryCode.Valid = false`)が入っている場合
func Test_calculatePopulation_null(t *testing.T) {
cities := []City{
{
CountryCode: sql.NullString{
String: "",
Valid: false,
},
Population: sql.NullInt64{
Int64: 100,
Valid: true,
},
},
{
CountryCode: sql.NullString{
String: "JPN",
Valid: true,
},
Population: sql.NullInt64{
Int64: 200,
Valid: true,
},
},
}
got := calculatePopulation(cities)
want := map[string]int64{
"JPN": 200,
}
// 長さが1になっているかどうかを確認する
if len(got) != len(want) {
t.Errorf("calculatePopulation(%v) = %v, want %v", cities, got, want)
}
// JPNの人口が100になっているかどうかを確認する
if got["JPN"] != want["JPN"] {
t.Errorf("calculatePopulation(%v) = %v, want %v", cities, got, want)
}
}