我是否应该始终将结构属性定义为可测试性的接口?



据我所知,在Go中模拟结构依赖的唯一方法是使用接口。所以我的问题是:当我的结构具有执行某些操作的方法时(当结构不仅仅是存储数据的模型时(,我是否应该始终将属性定义为接口以便正确模拟和测试它?

简单的例子:

type UserService struct {
userRepository UserRepository
}
func (us *UserService) MaleUsers() []User {
all := us.userRepository.FindAll()
maleUsers := []User{}
for _, u := range all {
if u.gender == "male" {
maleUsers = append(maleUsers, u)
}
}
return maleUsers
}

想象一下,我们有用户服务,它有一个依赖:存储库。 服务具有获取所有用户然后按某些条件过滤它们的方法。 顺便说一下,过滤逻辑可以放在单独的依赖项中,以避免在服务方法 (SRP( 中进行过滤。

顺便说一句。我来自Java世界。如果这种构建应用程序的方法在 Go 中不是意识形态的,请告诉我。

为了模拟UserServiceuserRepository依赖性,您认为最好的方法是使用接口是正确的。

首先,创建您的界面:

type UserRepository interface {
FindAll() []Users
}

然后构建一个模拟:

type MockUserRepository struct{}
func (mock MockUserRepository) FindAll() []Users {
// here you would manually build a slice of users and return it
return []Users
}

最后,将此模拟用作测试用例中的依赖项:

func TestMaleUsers(t *testing.T) {
// compose service using mock
service := UserService {
userRepository: MockUserRepository,
}
// get output of method call
users := service.MaleUsers()
// perform assertions on output
}

通过这种方式,您已经创建了一个接口模拟,可以在测试中使用,而无需对存储库执行任何数据库调用。

我想,如果你想正确执行单元测试,使用接口是合理的,正如你在问题中所说的那样。更重要的是,在Java或C#等其他语言中,这是一种自然的方式。但是,如果没有外部依赖项,您也可以轻松测试包的代码,而无需进行任何更改。

此外,我强烈建议您尽可能多地使用接口,这样您就可以实现更高的内聚力和更少的耦合,这将使您的代码更易于维护和测试。

在 Go 中,实体可以隐式满足接口,因此您将来需要能够模拟您的结构只需要添加一个接口声明。查看一些示例:

type MaleIterable interface {
MaleUsers() []User
}
type UserIterable interface {
FindAll() []User
}

这是一个代码片段,也可能对您有用。

package main
import "fmt"
type User struct {
Name string
Gender string
Age int
}
type UserIterable interface {
FindAll() []User
}
type FakeUserRepository struct {}
func (ur FakeUserRepository) FindAll() []User {
return []User{
User{Name:"Alex", Gender:"male", Age:19},
User{Name:"Max", Gender:"male", Age:34},
User{Name:"Maria", Gender:"female", Age:12},
}
}
type UserService struct {
userRepository UserIterable
}
func (us *UserService) MaleUsers() []User {
all := us.userRepository.FindAll()
maleUsers := []User{}
for _, u := range all {
if u.Gender == "male" {
maleUsers = append(maleUsers, u)
}
}
return maleUsers
}
func main() {
us := UserService{
userRepository: FakeUserRepository{},
}
males := us.MaleUsers()
fmt.Println(males)
}

最新更新