简体   繁体   English

在测试中如何从控制器中分离模型?

[英]How to separate models from controllers in testing?

So I wanted to isolate controllers from models in testing so that I could easily figure out stuff if troubles arise. 因此,我想在测试中将控制器与模型隔离开来,以便在出现问题时可以轻松找出问题所在。 Before, I just hit the endpoints with mock data but it was difficult to troubleshoot because the test runs from the router all the way to the datastore. 以前,我只是使用模拟数据访问端点,但是很难进行故障排除,因为测试从路由器一直运行到数据存储。 So I'm thinking maybe I'll just create two versions(MockController vs Controller) for each controller(and model) and use one depending on the value of the mode variable. 所以我想也许我将为每个控制器(和模型)创建两个版本(MockController vs Controller),并根据mode变量的值使用一个。 In a nutshell, this is how I plan to implement it. 简而言之,这就是我计划实现的方式。

const mode string = "test"

// UserModelInterface is the Interface for UserModel
type UserModelInterface interface {
    Get() 
}

// UserControllerInterface is the Interface for UserController
type UserControllerInterface interface {
    Login()
}

// NewUserModel returns a new instance of user model
func NewUserModel() UserModelInterface {
    if mode == "test" {
        return &MockUserModel{}
    } else {
        return &UserModel{}
    }
}

// NewUserController returns a new instance of user controller
func NewUserController(um UserModelInterface) UserControllerInterface {
    if mode == "test" {
        return &MockUserController{}
    } else {
        return &UserController{}
    }
}

type (
    UserController struct {um UserModelInterface}
    UserModel struct {}

    // Mocks
    MockUserController struct {um UserModelInterface}
    MockUserModel struct {}
)

func (uc *UserController) Login() {}
func (um *UserModel) Get() {}

func (uc *MockUserController) Login() {}
func (um *MockUserModel) Get() {}

func main() {
    um := NewUserModel()
    uc := NewUserController(um)
}

This way I could just skip sql query in the MockUserController.Login() and only validate the payload and return a valid response. 这样,我可以跳过MockUserController.Login()中的sql查询,仅验证有效负载并返回有效响应。

What do you think of this design? 您如何看待这种设计? Do you have a better implementation in mind? 您是否有更好的实现思路?

I would let the code that calls NewUserController() and NewUserModel() decide whether to create a mock or real implementation. 我会让调用NewUserController()和NewUserModel()的代码决定是创建模拟还是真实的实现。 If you use that pattern of dependency injection all the way up to the top your code will become clearer and less tightly coupled. 如果您一直使用这种依赖注入模式,那么代码将变得更加清晰和紧密。 Eg if the user controller is used by a server, it would look something like along the lines of: 例如,如果服务器使用用户控制器,则其外观类似于:

Real: 真实:

u := NewUserController() s := NewServer(u) u:= NewUserController()s:= NewServer(u)

In tests: 在测试中:

u := NewMockUserController() s := NewServer(u) u:= NewMockUserController()s:= NewServer(u)

I would try a more slim variant spread over a models and a controllers package, like this: 我会尝试在模型和控制器包上散布更苗条的变量,如下所示:

// inside package controllers

type UserModel interface {
    Get() // the methods you need from the user model
}

type User struct {
    UserModel
}

// inside package models

type User struct {
    // here the User Model
}


// inside package main

import ".....controllers"
import ".....models"

func main() {
    c := &controllers.User{&models.User{}}
}

// inside main_test.go
import ".....controllers"

type MockUser struct {

}


func TestX(t *testing.T) {
    c := &controllers.User{&MockUser{}}
}

For controller testing consider the ResponseRecorder of httptest package 对于控制器测试,请考虑httptest包ResponseRecorder

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM