2017-07-14 194 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