Go 테스트 - 어떤 항목이 호출되었는지 확인하는 방법은 무엇입니까?
저는 친구와 이야기를 나누며 Golang에서 여행을 시작할 때 직면했던 몇 가지 어려움을 기억하고 있었습니다. 컨텍스트를 위해 우리 둘 다
.toHaveBeenCalled()
, toHaveBeenCalledTimes(number)
및 .toHaveBeenCalledWith(...)
등과 같은 메서드를 제공하는 유명한 Jest와 같은 일부 테스트 프레임워크가 있는 땅인 NodeJS 배경에서 왔습니다.반면에 Go 세계에서 기본
testing
패키지는 (거의) 모든 단위 테스트 시나리오에 적합하지만 무언가가 호출되었는지 확인하는 것과 같은 작업을 수행하는 명확한 방법을 제공하지 않으며 플롯 트위스트는 다음과 같습니다. 그럴 필요가 없습니다. Go는 이와 같은 작업을 수행하는 훌륭한 관용적 방법을 제공하므로 프레임워크를 사용할 필요가 없습니다.좋아, 하지만 "Go idiomatic way"에서 호출된 항목을 확인하는 방법은 무엇입니까?
대답은 간단합니다. 인터페이스와 종속성 반전을 사용하는 것입니다!
종속성 역전(Dependency Inversion)에 대해 조금 이야기해 보겠습니다.
캡슐화의 모범 사례로 코드에서 가능할 때마다 인터페이스를 사용하여 유형을 추상화하는 것이 좋습니다. 예를 들어 다음과 같이 특정 품종의 개를 나타내는 구조체를 정의한다고 가정합니다.
package dogs
type DogLhasa struct {
Name string
}
func (d *DogLhasa) Bark() string {
return "woof-AUAU"
}
type DogRotweiler struct {
Name string
}
func (d *DogRotweiler) Bark() string {
return "woof-woof-ARHHHHHGG"
}
그리고 인수로 전달된 개의
Bark()
메서드를 호출하는 함수가 있다고 가정합니다. 문제는 이 함수를 정의하는 방법입니다. 인터페이스를 사용하지 않고 다음과 같이 이상한 작업을 수행해야 합니다.func MakeDogLhasaBark(dog DogLhasa) string {
return dog.Bark()
}
func MakeDogRotweilerBark(dog DogRotweiler) string {
return dog.Bark()
}
이것이 잘 확장되지 않는 것이 분명합니다. 코드에 추가하려는 모든 개 빵에 대해 이와 같은 함수를 작성해야 한다면 얼마나 엉망이 될지 상상해 보십시오.
이 문제를 해결하려면 인터페이스로 작업해야 합니다. 우리는 개가 무엇이어야 하는지를 정의하는 인터페이스를 정의하고 개를 다루는 모든 코드는 이 인터페이스를 중계할 수 있습니다.
type Dog interface {
Bark() string
}
func MakeDogBark(dog Dog) string {
return dog.Bark()
}
이제
MakeDogBark
를 호출할 때 인터페이스 Dog
를 구현하는 일부 구조체 유형의 객체를 전달할 수 있습니다.func main() {
dog := &DogLhasa{Name: "Rex"}
MakeDogBark(dog)
}
훨씬 간단하지 않습니까? 이를 종속성 역전이라고 합니다. 일부 특정 dog 구조체에 의존하는 대신 함수
MakeDogBark
는 구조체가 Dog
가 되기 위해 구현해야 하는 것을 정의하는 인터페이스Dog
에 의존합니다. 따라서 MakeDogBark
함수는 인터페이스Dog
를 구현하는 모든 구조체의 모든 개체를 수락할 수 있습니다. 종속성 반전의 시각적 표현을 확인하세요.그러나 이것이
toHaveBeenCalled.*
테스트와 같은 테스트를 구현하는 데 정확히 어떻게 도움이 될 수 있습니까?함수
MakeDogBark
에 대한 단위 테스트를 작성해 보겠습니다. 이 테스트에서는 MakeDogBark
가 호출될 때 인수로 전달된 Bark
의 dog
메서드가 호출되는지 확인하려고 합니다. 이를 위해 메서드Bark
가 호출된 횟수를 추적할 수 있는 "가짜 개"구조체를 만들 수 있습니다. 이와 같이:type fakeDog struct {
numberOfBarks int
}
func (d *fakeDog) Bark() string {
d.numberOfBarks++
return "woof"
}
이렇게 하면
numberOfBarks
유형의 개체가 짖을 때마다 변수fakeDog
가 증가합니다. 따라서 Bark
메서드가 호출되었는지 테스트하려면 numberOfBarks
가 0보다 큰지 확인해야 합니다.func TestMakeDogBark(t *testing.T) {
dog := &fakeDog{}
MakeDogBark(dog)
// fail if the dog didn't bark
if dog.numberOfBarks == 0 {
t.Errorf("Expected dog to bark once, but it barked %d times", dog.numberOfBarks)
}
}
유사한 휴리스틱을 사용하여
toHaveBenCalled.*
테스트의 변형을 달성할 수 있습니다. 예를 들어:toHaveBeenCalledWith
type fakeStruct struct {
calledWith string
}
func (f *fakeStruct) SomeMethod(s string) {
f.calledWith = s
}
func TestSomeMethod(t *testing.T) {
f := &fakeStruct{}
f.SomeMethod("hello")
if f.calledWith != "hello" {
t.Errorf("Expected SomeMethod to be called with 'hello', but was called with '%s'", f.calledWith)
}
}
toHaveBeenCalledTimes
type fakeStruct struct {
calledTimes int
}
func (f *fakeStruct) SomeMethod() {
f.calledTimes++
}
func TestSomeMethod(t *testing.T) {
f := &fakeStruct{}
f.SomeMethod()
f.SomeMethod()
f.SomeMethod()
if f.calledTimes != 3 {
t.Errorf("Expected f.calledTimes to be 3, but it was %d", f.calledTimes)
}
}
꽤 멋지죠? 타사 프레임워크나 라이브러리 없이 네이티브 항목만 사용하여 Golang으로 얼마나 많은 것을 달성할 수 있는지 정말 놀랍습니다.
행복한 테스트! 🧪
Reference
이 문제에 관하여(Go 테스트 - 어떤 항목이 호출되었는지 확인하는 방법은 무엇입니까?), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/gustavolopess/go-tests-how-to-verify-if-something-was-called-46j9텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)