处理模型特定操作与数据库操作

时间:2018-05-29 16:20:18

标签: go webserver

我在模型设计方面遇到一些问题,特别是处理模型特定操作 vs 数据库操作。一个很好的例子就是我的用户模型。

在我的数据库中创建用户时,我想:

  1. 验证密码是否符合条件(模型操作)
  2. 创建摘要(模型操作)
  3. 设置时间戳(模型操作)
  4. 将电子邮件,摘要和时间戳保存到数据库(数据库操作)
  5. 测试时,我显然希望对所有4个进行一组单元测试,但是#4调用其他3个,我不想重新测试,或者如果其中任何一个有#4测试失败的风险3做。

    我已经为ModelActions和StoreActions创建了一个单独的接口,并在需要时将UserAction接口发送到商店操作,但是当我写出来时,我已经感觉到了一些严重的代码味道。

    User

    是否有更好的方法来模拟这些单独的操作,以便在测试DB / Store功能时可以模拟UserActions?我尝试将type UserAction { SetTimestamps() CreateDigest() (string, error) VerifyPassword() error ComparePassword(password string) error User() *User } 结构存储为接口的一部分,例如:

    {{1}}

    然而,这会在创建模拟时导致周期性导入,并且还会打开所有字段,因为模型的字段是可导出的,所以这些字段已经可以使用

1 个答案:

答案 0 :(得分:1)

认为您的用户应该是具体类型,您应该使用界面来模拟您的商店。

例如,像这样的项目结构对我有意义:

cmd/
   server/
      user.go
      user_test.go
      main.go
      store.go
mysql/
   mysql.go
   user.go
   user_test.go
user.go
user_test.go

您的用户模型位于user.go的根目录中。此文件将包含您的User结构,以及将在其上运行的函数,如CreateDigest。应在user_test.go

中测试这些功能

值得一提的是,在您的根目录中,您的软件包不应该是main,您的软件包名称应该是项目的名称,我们将其称为myapp

您的mysqlpostgres等也应该是具体实现。您可以在该包中使用以下功能:

func (m *MySQL) InsertUser(u *myapp.User) error

应在mysql/user_test.go

中测试此功能

最后,我们可以在server中将它们放在一起。这是您实际部署或运行​​的二进制文件。

cmd/server/store.go中,您应该创建一个由mysql实现的界面。

cmd/server/user_test.go中,很容易模拟这个,这样你就不必点击真正的数据库了。我相信你的界面应该存在于你的客户端。在这种情况下,servermysql的客户。

cmd/server/user.go中,您的功能可能如下所示:

func CreateUser(w http.ResponseWriter, r *http.Request) {
  var u myapp.user
  err := json.NewDecoder(r.Body).Decode(&u)
  if err != nil {
    panic(err) // don't do this for real
  }

  d := myapp.CreateDigest(u.Password)
  u.Digest = d

  // s is the interface, defined in `cmd/server/store.go`, but is implemented by mysql
  err = s.InsertUser(&u)
  if err != nil {
    panic(err)
  }

  // Since we pass a pointer, you can have your store set the ID of the user
  fmt.Println(u.ID)
}

现在您可以更好地分离关注点,一切都应该易于测试,并且对现有代码进行更改很容易。