2017-07-14 93 views
2

基本上我已经开始在Riot Games API的包装上工作了,我正在努力如何测试它。我已经将存储库插入到Travis中,所以它推动它运行go test,但我不知道如何去测试它,因为请求所需的API_KEY每天都会更改,我无法自动重新生成它,我会如果我直接测试端点,则每天手动添加它。我会如何测试这种方法?

所以我想知道是否有可能嘲笑的反应,但在这种情况下,我猜我需要重构我的代码?

所以我做了一个结构来代表他们的SummonerDTO

type Summoner struct { 
    ID   int64 `json:"id"` 
    AccountID  int64 `json:"accountId"` 
    ProfileIconID int `json:"profileIconId"` 
    Name   string `json:"name"` 
    Level   int `json:"summonerLevel"` 
    RevisionDate int64 `json:"revisionDate"` 
} 

这结构有一个方法:

func (s Summoner) ByName(name string, region string) (summoner *Summoner, err error) { 
    endpoint := fmt.Sprintf("https://%s.api.riotgames.com/lol/summoner/%s/summoners/by-name/%s", REGIONS[region], VERSION, name) 

    client := &http.Client{} 
    req, err := http.NewRequest("GET", endpoint, nil) 
    if err != nil { 
     return nil, fmt.Errorf("unable to create new client for request: %v", err) 
    } 

    req.Header.Set("X-Riot-Token", API_KEY) 

    resp, err := client.Do(req) 
    if err != nil { 
     return nil, fmt.Errorf("unable to complete request to endpoint: %v", err) 
    } 

    defer resp.Body.Close() 

    if resp.StatusCode != 200 { 
     return nil, fmt.Errorf("request to api failed with: %v", resp.Status) 
    } 

    respBody, err := ioutil.ReadAll(resp.Body) 
    if err != nil { 
     return nil, fmt.Errorf("unable to read response body: %v", err) 
    } 

    if err := json.Unmarshal([]byte(respBody), &summoner); err != nil { 
     return nil, fmt.Errorf("unable to unmarshal response body to summoner struct: %v", err) 
    } 

    return summoner, nil 
} 

是它的结构方法,没有一个单一的责任的情况下, ,从某种意义上说,它正在构建端点,引发请求并解析响应。我是否需要重构它才能使其可测试,并且在这种情况下,最佳方法是什么?我应该做一个请求和响应结构,然后测试它们吗?

要澄清API密钥使用率是有限的,需要每天重新生成,Riot Games不允许您使用抓取工具来自动重新生成密钥。我正在使用Travis进行持续集成,所以我想知道是否有方法来模拟请求/响应。

潜在我的方法是错误的,仍然在学习。

希望所有人都能提供某种形式的感觉,如果不是,我们很乐意澄清。

回答

4

编写单元测试包括:

  • 提供您的所有输入已知状态。
  • 测试一下,给定所有这些输入的含义组合,就会得到预期的输出结果。

所以,你需要先确定您的输入:

  • s Summoner
  • name string
  • region string

加任何 “隐藏” 的投入,以全局的方式:

  • client := &http.Client{}

而且你的输出是:

  • summoner *Summoner
  • err error

(也可能有 “隐藏” 的输出,如果你写的文件,或者更改全局变量,但你似乎没有这样做)。

现在前三个输入很容易从头开始为您的测试创建:只需提供一个空的Summoner{}(因为您没有在函数中读取或设置该函数,所以不需要将其设置为除了空值)。 nameregion可以简单地设置为字符串。

剩下的唯一部分是你的http.Client。至少,你应该把它作为一个参数来传递。这不仅可以让您控制测试,还可以让您在将来的生产中轻松使用不同的客户端。

但为了简化测试,您可能会考虑实际传入类似客户端的界面,您可以轻松地进行模拟。你client调用的唯一方法是Do,所以你可以很容易地创建一个Doer接口:

type doer interface { 
    Do(req *Request) (*Response, error) 
} 

然后改变你的函数签名,把这个当作一个说法:现在

func (s Summoner) ByName(client doer, name string, region string) (summoner *Summoner, err error) { 

,在您的测试您可以创建一个满足doer界面的自定义类型,并以您喜欢的任何http.Response作为响应,而无需在测试中使用服务器。

+0

感谢您的回复,对我来说看起来像一个彻底和深思熟虑的过程。在我接受答案之前,会给予其他人一点时间提醒。 – Mikey