英文:
How to set an interface for a custom HTTP client?
问题
我正在为我导入的这个第三方库编写测试时遇到了困难。我认为问题出在我想让我的CustomClient
结构体拥有一个client
接口,而不是*banker.Client
。这使得测试变得非常困难,因为很难模拟一个*banker.Client
。你有什么办法可以正确地将其转换为接口吗?这样我就可以轻松地编写针对它的模拟测试并设置一个假的客户端了。
type CustomClient struct {
client *banker.Client //我想将其改为一个接口
name string
address string
}
func (c *CustomClient) SetHttpClient(httpClient *banker.Client) { //我想接受一个接口,这样我就可以轻松地模拟它。
c.client = httpClient
}
问题在于banker.Client
是一个我导入的第三方客户端,它包含许多结构体和其他字段。它的结构如下:
type Client struct {
*restclient.Client
Monitor *Monitors
Pricing *Pricing
Verifications *Verifications
}
最终,我的代码看起来像这样:
func (c *CustomClient) RequestMoney() {
_, err := v.client.Verifications.GetMoney("fakeIDhere")
}
英文:
I am having difficulty writing tests for this 3rd party library I am importing. I think this is because I want my CustomClient
struct to have a client
interface instead of the *banker.Client
. This is making testing very difficult because it's hard to mock a *banker.Client
. Any ideas how I can correctly turn this into an interface? So I can easily write mock tests against it and set up a fake client?
type CustomClient struct {
client *banker.Client //I want to change this to an interface
name string
address string
}
func (c *CustomClient) SetHttpClient(httpClient *banker.Client) { //I want to accept an interface so I can easily mock this.
c.client = httpClient
}
The problem is that banker.Client is a third party client I am importing with many structs and other fields inside of it. It looks like this:
type Client struct {
*restclient.Client
Monitor *Monitors
Pricing *Pricing
Verifications *Verifications
}
The end result is that my code looks like this:
func (c *CustomClient) RequestMoney() {
_, err := v.client.Verifications.GetMoney("fakeIDhere")
}
答案1
得分: 1
给定在结构体上的字段方法,这肯定不是一个简单的解决方案。然而,我们可以尝试减少当前包中冗长的测试用例。
在你的工作包和银行家之间添加另一个层次(包)。为了解释,简化示例中的代码。
假设你的banker
包有以下代码:
type Client struct {
Verification *Verification
}
type Verification struct{}
func (v Verification) GetMoney(s string) (int, error) {
...
}
创建另一个导入银行家并定义接口的包,比如bankops
包:
type Bank struct {
BankClient *banker.Client
}
type Manager interface {
GetMoney(s string) (int, error)
}
func (b *Bank) GetMoney(s string) (int, error) {
return b.BankClient.Verification.GetMoney(s)
}
> 注意:实际问题(没有接口的测试)仍然存在于bankops包中,但这样更容易测试,因为我们只是转发结果。达到了单元测试的目的。
最后,在当前包中(对我来说,是main
包),我们可以这样做:
type CustomClient struct {
client bankops.Manager
}
func (c *CustomClient) RequestMoney() {
_, err := c.client.GetMoney("fakeIDhere")
...
}
func main() {
client := &CustomClient{
client: &bankops.Bank{
BankClient: &banker.Client{
Verification: &banker.Verification{},
},
},
}
client.RequestMoney()
}
要查看工作示例,请在Playground中检查。
你可以像在原始代码片段中所做的那样添加setters
或builders pattern
,将字段(如BankerClient
)设为非导出的。
英文:
Given methods over fields on the struct, it sure wouldn't be a simple solution. However, we can try to minimize the lengthy test cases on the current package.
Add another layer (package) between your working package and banker. Simplifying the code in example to explain.
Let's say your banker
package has the following code:
type Client struct {
Verification *Verification
}
type Verification struct{}
func (v Verification) GetMoney(s string) (int, error) {
...
}
Create another package that imports the banker and has interface defined, say bankops
package:
type Bank struct {
BankClient *banker.Client
}
type Manager interface {
GetMoney(s string) (int, error)
}
func (b *Bank) GetMoney(s string) (int, error) {
return b.BankClient.Verification.GetMoney(s)
}
> Note: The actual issue (test without interface) is still here in bankops package, but this is easier to test as we are only forwarding the result. Serves the purpose of unit tests.
Finally, in the current package (for me, it is main
package), we can
type CustomClient struct {
client bankops.Manager
}
func (c *CustomClient) RequestMoney() {
_, err := c.client.GetMoney("fakeIDhere")
...
}
func main() {
client := &CustomClient{
client: &bankops.Bank{
BankClient: &banker.Client{
Verification: &banker.Verification{},
},
},
}
client.RequestMoney()
}
For working example, check in Playground.
You may add the setters
or builders pattern
as you were doing in your original code snippet to make the fields (like BankerClient
) unexported.
答案2
得分: 0
我认为直接将其转换为接口是不可能的,因为我们应该使用Client
的成员变量。
将其成员变量转换为接口怎么样?
例如,
for _, test := []struct{}{
testVerification VerificationInterface
}{{
testVerification: v.Client.Verifications
},{
testVerification: VerficationMock
}}{
// 在这里编写测试代码
}
英文:
I think it is impossible to make it into interface directly
because we should use the member variables of the Client
.
How about making its member into interface?
For example,
for _, test := []struct{}{
testVerification VerificationInterface
}{{
testVerification: v.Client.Verifications
},{
testVerification: VerficationMock
}}{
// test code here
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论