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

时间:2017-07-23 08:02:04

标签: go

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

简单示例:

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中没有意义,请告诉我。

2 个答案:

答案 0 :(得分:1)

为了模仿userRepository的{​​{1}}依赖关系,您认为最佳方法是使用界面是正确的。

首先,创建你的界面:

UserService

然后建立一个模拟:

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
}

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

答案 1 :(得分:0)

我想,如果你想要正确执行单元测试,使用接口是合理的,正如你在问题中所说的那样。更重要的是,在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)
}