英文:
golang interfaces for testing
问题
我正在尝试在我的代码中创建一个数据库模拟,然后我正在为代码引入接口,以创建模拟:
这是我的代码(我不知道这是否是正确的方法)
package interfaces
type ObjectAPI interface {
FindSomethingInDatabase(ctx context.Context, name string) (e.Response, error)
}
我的接口实现如下:
package repositories
func FindSomethingInDatabase(ctx context.Context, name string) (e.Response, error) {
statement, err := db.SqlStatementWithCtx(ctx,
`SELECT *
FROM table
WHERE name = ? LIMIT 1`)
row := statement.QueryRowContext(ctx, name)
if err != nil {
return e.Response{}, err
}
statement.Close()
return toResponse(row), nil //这个方法将数据库行转换为e.Response结构体
}
现在,我需要从一个方法中调用FindSomethingInDatabase
的实现,然后我会接收一个对象类型的接口:
func CallImplementation(request *dto.XRequest, repo i.ObjectAPI) dto.XResponse{
result := repo.FindSomethingInDatabase(request.Ctx, request.Name)
// 更多代码
}
但是现在我不知道如何调用CallImplementation
来传递一个带有实现的对象。
调用该方法时传递接口的实现。
英文:
I am trying to create a database mock in my code, then i am introducing interfaces to my code, to create the mock:
This is my code (I don't know if it's the correct approach)
package interfaces
type ObjectAPI interface {
FindSomethingInDatabase(ctx context.Context, name string) (e.Response, error)
}
And my implementation of the interface is:
package repositories
func FindSomethingInDatabase(ctx context.Context, name string) (e.Response, error) {
statement, err := db.SqlStatementWithCtx(ctx,
`SELECT *
FROM table
WHERE name = ? LIMIT 1`)
row := statement.QueryRowContext(ctx, name)
if err != nil {
return e.Response{}, err
}
statement.Close()
return toResponse(row), nil //this method convert row database to e.Response struct
}
Now I need call from one method the implementation of my FindSomethingInDatabase, then i am receiving an object type interface:
func CallImplementation(request *dto.XRequest, repo i.ObjectAPI) dto.XResponse{
result := repo.FindSomethingInDatabase(request.Ctx, request.Name)
// more code
}
But now I don't know how can I call CallImplementation
` to pass an object with the implementation.
Call the method passing the implementation of the interface
答案1
得分: 0
一个接口描述了一个类型。由于你的FindSomethingInDatabase
实现只是一个没有接收者的函数,所以没有类型实现了ObjectAPI
接口。
你可以将类型为func(ctx context.Context, name string) (e.Response, error)
的值作为回调传递给CallImplementation
,并完全摒弃接口。或者保留接口,定义一个类型,并将该类型作为当前FindSomethingInDatabase
实现的接收者。然后你可以将该类型传递给CallImplementation
,因为它现在实现了ObjectAPI
接口。以下是后一种方法的示例(这是我首选的可扩展性选项):
type database struct {}
func (d *database) FindSomethingInDatabase(ctx context.Context, name string) (e.Response, error) {
// ...
}
func CallImplementation(request *dto.XRequest, repo i.ObjectAPI) dto.XResponse{
result := repo.FindSomethingInDatabase(request.Ctx, request.Name)
// more code
}
func main() {
db := &database{}
_ = Callimplementation(getRequest(), db)
}
在这种情况下,你可能希望将db
作为database
的成员变量存储,而不是作为全局变量(目前似乎是这种情况)。
英文:
An interface describes a type. Since your FindSomethingInDatabase
implementation is just a func without a receiver, there is no type that implements interface ObjectAPI
.
You can pass a value of type func(ctx context.Context, name string) (e.Response, error)
into CallImplementation
as a callback and get rid of the interface altogether. Alternatively, keep the interface, define a type, and make that type the receiver for your current FindSomethingInDatabase
implementation. You can then pass the type to CallImplementation
, since it will now implement the ObjectAPI
interface. An example of the latter (which would be my preferred option for extensibility):
type database struct {}
func (d *database) FindSomethingInDatabase(ctx context.Context, name string) (e.Response, error) {
// ...
}
func CallImplementation(request *dto.XRequest, repo i.ObjectAPI) dto.XResponse{
result := repo.FindSomethingInDatabase(request.Ctx, request.Name)
// more code
}
func main() {
db := &database{}
_ = Callimplementation(getRequest(), db)
}
In this case, you will probably want to store db
as a member of database
, rather than having it as a global variable (which appears to be the case now).
答案2
得分: 0
参考mockery。它提供了接口的自动生成模拟对象的功能,并可以作为模拟的最佳实践的参考。
通常你会这样做:
api.go:
type API interface {
Something(ctx context.Context, name string) (e.Response, error)
}
type ApiImpl struct {
}
func (t *ApiImpl) Something(ctx context.Context, name string) (e.Response, error) {
// 实现
}
api_test.go
// 模拟对象可以手动编写,也可以使用mockery自动生成
type MockImpl struct {
}
func (m *MockImpl) Something(ctx context.Context, name string) (e.Response, error) {
// 模拟实现
}
func TestSomething(t * testing.T) {
// 使用模拟对象的测试代码
}
英文:
refer mockery. it provides auto generation of mocks for interfaces and can be good reference about best practices for mocking.
typically you would do this:
api.go:
type API interface {
Something(ctx context.Context, name string) (e.Response, error)
}
type ApiImpl struct {
}
func (t *ApiImpl) Something(ctx context.Context, name string) (e.Response, error) {
// impl
}
api_test.go
// mocks can be hand coded or autogenerated using mockery
type MockImpl struct {
}
func (m *MockImpl) Something(ctx context.Context, name string) (e.Response, error) {
// mock implementation
}
func TestSomething(t * testing.T) {
// test code with mocks
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论