![[Learn Go with Tests] 슬라이스 및 배열](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyqJRO%2FbtsICXs82Bu%2Fpq5cBON15Sbj7qkBs4BghK%2Fimg.png)
해당 포스팅은 Learn Go with Tests Gitbook을 따라 실습한 내용을 정리한 문서입니다.
단일 슬라이스의 합 리턴하기
package main
import "testing"
func TestSum(t *testing.T) {
numbers := [5]int{1, 2, 3, 4, 5}
got := sum(numbers)
want := 15
if got != want {
t.Errorf("got %d want %d given, %v", got, want, numbers)
}
}
위와 같이 테스트코드를 작성한다.
- go 에서는 다음과 같은 방법으로 배열을 선언할 수 있다.
- [N]type{value1, value2, ..., valueN} e.g. numbers := [5]int{1, 2, 3, 4, 5}
- [배열의 크기] 자료형{배열 값}
- [...]type{value1, value2, ..., valueN} e.g. numbers := [...]int{1, 2, 3, 4, 5}
- 위와 같이 배열의 크기를 선언하지 않을 수도 있다.
- [N]type{value1, value2, ..., valueN} e.g. numbers := [5]int{1, 2, 3, 4, 5}
테스트를 실행할 수 있는 코드 작성
package main
func Sum(numbers [5]int) int {
sum := 0
for i := 0; i < 5; i++ {
sum += numbers[i]
}
return sum
}
- 배열의 특정한 값을 가져오기 위해 array[index]라는 문법을 사용해서 가져올 수 있다.
리팩토링
func Sum(numbers [5]int) int {
sum := 0
for _, number := range numbers {
sum += number
}
return sum
}
- range는 배열의 반복적인 처리가 가능하다.
- 인덱스, 값 두개를 가져오는 반복이지만 여기서는 idx를 사용하지 않아 _(여백 식별자)로 인덱스 값을 무시한다.
⇒ 배열의 경우 고정된 크기를 가지기 때문에 불편함을 유발할 수 있다. 이때, Go 언어에서는 슬라이스를 통해 컬렉션의 크기를 인코딩 하지 않아 아무 크기나 가져도 가능한 도구를 사용할 수 있다.
Slice 테스트 작성하기
package main
import "testing"
func TestSum(t *testing.T) {
t.Run("collection of 5 numbers", func(t *testing.T) {
numbers := [5]int{1, 2, 3, 4, 5}
got := Sum(numbers)
want := 15
if got != want {
t.Errorf("got %d want %d given, %v", got, want, numbers)
}
})
t.Run("collection of any size", func(t *testing.T) {
numbers := []int{1, 2, 3}
got := Sum(numbers)
want := 6
if got != want {
t.Errorf("got %d want %d given, %v", got, want, numbers)
}
})
}
테스트 코드를 다음과 같이 작성되면 컴파일 되지 않는다! 이때 Sum을 배열 대신 슬라이스로 변경하면 배열 또한 컴파일이되지 않게 되어 새로운 함수를 만들거나, 배열을 슬라이스로 만들거나를 선택하면 된다.
⇒ 현재의 경우 누가 우리의 함수를 사용하는 것이 아니므로, 두개를 유지하는 것이 아닌 하나만 유지하도록 한다.
func Sum(numbers []int) int {
sum := 0
for _, number := range numbers {
sum += number
}
return sum
}
다음과 같이 슬라이스로 바꾸어 주면 된다.
Test Coverage
Go 언어의 내장된 테스트 도구로 커버리지를 테스트 하는 것이 가능하다
go test -cover
다음과 같이 커버리지를 확인하는 것이 가능하다.
여러 슬라이스의 합 리턴하기
테스트 작성하기
func TestSumAll(t *testing.T) {
got := SumAll([]int{1, 2}, []int{0, 9})
want := []int{3, 9}
if got != want {
t.Errorf("got %v want %v", got, want)
}
}
최소한의 코드 작성하기
func SumAll(numbersToSum ...[]int)(sums []int){
return
}
- …[]int를 활용하여 변동 가능한 int형 배열 여러개를 받을 수 있다.
slice 값 비교하기
go에서는 슬라이스를 한번에 비교하지 않는다. 이때 got과 want를 반복하여 매번 비교할수는 있지만, 이렇게 하면 비효율적이고 번거롭다. 따라서 any 타입이나 비교가 가능한 reflect.DeepEqual을 사용한다.
func TestSumAll(t *testing.T) {
got := SumAll([]int{1, 2}, []int{0, 9})
want := []int{3, 9}
if !reflect.DeepEqual(got, want){
t.Errorf("got %v want %v", got, want)
}
}
- 이때 reflect.DeepEqual은 type safe 하지 않기에, 슬라이스와 문자열을 비교하려고 해도 컴파일이 된다.
func TestSumAll(t *testing.T) {
got := SumAll([]int{1, 2}, []int{0, 9})
want := "bob"
if !reflect.DeepEqual(got, want) {
t.Errorf("got %v want %v", got, want)
}
}
다음과 같이 bob이라는 문자열과, got으로는 slice를 받아도 컴파일이 가능하다. 따라서, 슬라이스를 비교할때 DeepEqual은 매우 편리하지만 사용할 때에 주의가 필요하다.
SumAll 코드 작성하기
func SumAll(numbersToSum ...[]int) []int {
lengthOfNumbers := len(numbersToSum)
sums := make([]int, lengthOfNumbers)
for i, numbers := range numbersToSum {
sums[i] = Sum(numbers)
}
return sums
}
- make 함수 → numbersToSum의 len 만큼의 시작 크기를 갖는 슬라이스 만들어주기
- muSlice[N]과 같이 인덱스 사용이 가능하고, 새 값을 =으로 할당하는 것 또한 가능하다.
리팩토링
func SumAll(numbersToSum ...[]int) []int {
var sums []int
for _, numbers := range numbersToSum {
sums = append(sums, Sum(numbers))
}
return sums
}
- 새 값이 들어간 슬라이스를 리턴하는 append 함수를 사용하면 편리하게 코드를 작성하는 것이 가능하다.
SumAllTails : 모든 슬라이스의 꼬리 합 계산하기
테스트 코드 작성하기
func TestSumAllTails(t *testing.T){
got := SumAllTails([]int{1,2}, []int{0,9})
want := []int{2,9}
if!reflect.DeepEqual(got, want){
t.Errorf("got %v want %v", got, want)
}
}
SumAllTails
func SumAllTails(numbersToSum ...[]int) []int {
var sums []int
for _, numbers := range numbersToSum {
tail := numbers[1:]
sums = append(sums, Sum(tail))
}
return sums
}
- slice 또한 slice[low:high]를 통해 값을 자를 수 있다
- : 옆의 값을 누락하면 값부터 끝까지 가져오는 것이 가능하다.
⇒ 이때 만약 비어있는 리스트를 반환한다면 어떻게 될까?
이 또한 테스트로 검증해보자!
빈 리스트 테스트로 검증하기
func TestSumAllTails(t *testing.T) {
t.Run("make the sums of some slices", func(t *testing.T) {
got := SumAllTails([]int{1, 2}, []int{0, 9})
want := []int{2, 9}
if !reflect.DeepEqual(got, want) {
t.Errorf("got %v want %v", got, want)
}
})
t.Run("safely sum empty slices", func(t *testing.T) {
got := SumAllTails([]int{}, []int{3, 4, 5})
want := []int{0, 9}
if !reflect.DeepEqual(got, want) {
t.Errorf("got %v want %v", got, want)
}
})
}
⇒ 비어있는 슬라이스를 주입하자 런타임 에러가 발생한다!! 기존의 코드를 다시 수정해보자
func SumAllTails(numbersToSum ...[]int) []int {
var sums []int
for _, numbers := range numbersToSum {
if len(numbers) == 0{
sums = append(sums, 0)
}else{
tail := numbers[1:]
sums = append(sums, Sum(tail))
}
}
return sums
}
⇒ 패스하는 것을 볼 수 있다!
테스트 코드 리팩토링
assertion을 하는 부분에서 중복된 DeepEqual 부분의 코드가 존재한다. 해당 부분을 함수로 추출해보자.
package main
import (
"reflect"
"testing"
)
func TestSumAllTails(t *testing.T) {
checkSums := func(t testing.TB, got, want []int) {
t.Helper()
if !reflect.DeepEqual(got, want) {
t.Errorf("got %v want %v", got, want)
}
}
t.Run("make the sums of some slices", func(t *testing.T) {
got := SumAllTails([]int{1, 2}, []int{0, 9})
want := []int{2, 9}
checkSums(t, got, want)
})
t.Run("safely sum empty slices", func(t *testing.T) {
got := SumAllTails([]int{}, []int{3, 4, 5})
want := []int{0, 9}
checkSums(t, got, want)
})
}
⇒ 이렇게 하면 deepEqual의 부작용인 type-safety를 조금 더 올려주는 방식이 된다.
'Language > Go' 카테고리의 다른 글
[Learn Go with Tests] 포인터 & 에러 (1) | 2024.07.24 |
---|---|
[Learn Go with Tests] 구조체, 메서드 & 인터페이스 (0) | 2024.07.18 |
[Learn Go with Tests] Iteration (0) | 2024.07.18 |
[Learn Go with Tests] Integer (1) | 2024.07.18 |
[Learn Go with Tests] Hello World 및 테스트 실행 방법 (0) | 2024.07.18 |
보안 전공 개발자지만 대학로에서 살고 싶어요
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!