Golang单元测试:模拟嵌套结构体的方法

huangapple go评论79阅读模式
英文:

Golang Unit Test Mock method of nested struct

问题

我想模拟一个嵌套结构体的方法。我尝试定义一个接口并让模拟对象实现它,但是我无法使其正常工作。

这是我想要测试的结构体:

type OuterThing struct {
	innerThing *InnerThing
}

func (a *OuterThing) doLotsOfStuff() {
	println("i am doing")
	u, err := a.innerThing.DoStuff("lots of stuff")
	if err != nil {
		println("ran into an error, also doing some logic")
	}
	println("and more", u)
}

我想要模拟其DoStuff()函数的嵌套结构体如下:

type InnerThing struct {
	name string
}

func (b *InnerThing) DoStuff(x string) (uint64, error) {
	println("i want to mock this method")
	return 0, nil
}

小小的曲折:我不能更改这些结构体及其方法的代码。

为了更清楚地表达我的观点,我编写了以下测试:

func TestDoLotsOfStuff(t *testing.T) {
	testCases := []struct {
		name            string
		structUnderTest *OuterThing
	}{
		{
			name: "happy case",
			structUnderTest: &OuterThing{
				innerThing: &InnerThing{name: "I am the inner thing."},
			},
		},
		{
			name: "error case",
			structUnderTest: &OuterThing{
				innerThing: &InnerThing{name: "i should be a mock with a mocked DoStuff function"},
			},
		},
	}

	for _, testCase := range testCases {
		t.Run(testCase.name, func(t *testing.T) {
			testCase.structUnderTest.doLotsOfStuff()
			// assertions
		})
	}
}

我对Java和Mockito非常熟悉,这将是一个相当简单的任务。我知道Go语言在隐式接口和没有类等方面有很多不同,但这真的是一个如此罕见的用例吗?

英文:

I want to mock a method of a nested struct. I have tried to define an interface and make the Mock implement it, but I could not get it working.

This is the struct I want to test:

type OuterThing struct {
	innerThing *InnerThing
}

func (a *OuterThing) doLotsOfStuff() {
	println("i am doing")
	u, err := a.innerThing.DoStuff("lots of stuff")
	if err != nil {
		println("ran into an error, also doing some logic")
	}
	println("and more", u)
}

The nested struct, of which I want to mock its DoStuff() function, looks like this:

type InnerThing struct {
	name string
}

func (b *InnerThing) DoStuff(x string) (uint64, error) {
	println("i want to mock this method")
	return 0, nil
}

Little side twist: I can not change the code of these structs and their methods.

To make my point a bit more clear i have written this test:

func TestDoLotsOfStuff(t *testing.T) {
	testCases := []struct {
		name            string
		structUnderTest *OuterThing
	}{
		{
			name: "happy case",
			structUnderTest: &OuterThing{
				innerThing: &InnerThing{name: "I am the inner thing."},
			},
		},
		{
			name: "error case",
			structUnderTest: &OuterThing{
				innerThing: &InnerThing{name: "i should be a mock with a mocked DoStuff function"},
			},
		},
	}

	for _, testCase := range testCases {
		t.Run(testCase.name, func(t *testing.T) {
			testCase.structUnderTest.doLotsOfStuff()
			// assertions
		})
	}
}

I am quite familiar with Java and Mockito and this would be a pretty trivial task. I know Go has lots of differences with its implicit interfaces and no classes et cetera, but is this really such an uncommon usecase?

答案1

得分: 2

你可以通过使用接口和依赖注入来实现。假设你有一个名为Inner的接口,其中包含DoStuff()方法。OuterThing结构体应该包含这个接口,而不是InnerThing结构体。现在,你可以使用依赖注入将Inner接口传递给OuterThing结构体。

type Inner interface {
	DoStuff(string) (uint64, error)
}

type OuterThing struct {
	innerThing Inner
}

func NewOuterThing(in Inner) *OuterThing {
	return &OuterThing{
		innerThing: in,
	}
}

现在,在测试中,你可以创建mockInnerThing结构体并实现模拟的DoStuff()方法,然后将mockInnerThing传递给OuterThing结构体。

type MockInnerThing struct {
	name string
}

func NewMockInnerThing(name string) *MockInnerThing {
	return &MockInnerThing{
		name: name,
	}
}

func (b *MockInnerThing) DoStuff(x string) (uint64, error) {
	println("Mock!")
	return 0, nil
}
func TestDoLotsOfStuff(t *testing.T) {
    mit := NewMockInnerThing("Test")
	ot := NewOuterThing(mit)
	ot.doLotsOfStuff()
}
英文:

You can do it by using an Interface and Dependency Injection.
Let's say, you have an Interface called Inner that contains the DoStuff() method. The OuterThing struct should contain this Interface instead of the struct InnerThing. Now, you can use dependency injection to pass the Inner Interface to this OuterThing struct.

type Inner interface {
	DoStuff(string) (uint64, error)
}

type OuterThing struct {
	innerThing Inner
}

func NewOuterThing(in Inner) *OuterThing {
	return &OuterThing{
		innerThing: in,
	}
}

Now, in the test, you can create the mockInnerThing struct and implement the mock DoStuff() method and pass the mockInnerThing to the OuterThing struct.

type MockInnerThing struct {
	name string
}

func NewMockInnerThing(name string) *MockInnerThing {
	return &MockInnerThing{
		name: name,
	}
}

func (b *MockInnerThing) DoStuff(x string) (uint64, error) {
	println("Mock!")
	return 0, nil
}
func TestDoLotsOfStuff(t *testing.T) {
    mit := NewMockInnerThing("Test")
	ot := NewOuterThing(mit)
	ot.doLotsOfStuff()
}

huangapple
  • 本文由 发表于 2022年9月12日 03:40:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/73682187.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定