как вы можете блокировать вызовы на GitHub для тестирования?

# #unit-testing #go #stub

Вопрос:

Мне нужно создать комментарий запроса на вытягивание с помощью go-github, и мой код работает, но теперь я хотел бы написать тесты для него (да, я знаю, что тесты должны быть первыми), чтобы я на самом деле не вызывал настоящую службу GitHub во время тестирования.

Я прочитал 3 блога о том, как голанг колется и издевается, но, будучи новичком в голанге, я немного растерялся, несмотря на это обсуждение проблем go-github. Например, я написал следующую функцию:

 // this is my function
func GetClient(token string, url string) (*github.Client, context.Context, error) {
    ctx := context.Background()
    ts := oauth2.StaticTokenSource(
        amp;oauth2.Token{AccessToken: token},
    )
    tc := oauth2.NewClient(ctx, ts)
    client, err := github.NewEnterpriseClient(url, url, tc)

    if err != nil {
        fmt.Printf("error creating github client: %q", err)
        return nil, nil, err
    }
    return client, ctx, nil
}
 

Как я мог это заглушить?

Аналогично, у меня есть это:

 func GetPRComments(ctx context.Context, client *github.Client) ([]*github.IssueComment, *github.Response, error)  {
    opts := amp;github.IssueListCommentsOptions{
        ListOptions: github.ListOptions{
            Page:    1,
            PerPage: 30,
        },
    }
    githubPrNumber, err := strconv.Atoi(os.Getenv("GITHUB_PR_NUMBER"))
    if err != nil || githubPrNumber == 0 {
      panic("error: GITHUB_PR_NUMBER is not numeric or empty")
    }

    // use Issues API for PR comments since GitHub docs say "This may seem counterintuitive... but a...Pull Request is just an Issue with code"
    comments, response, err := client.Issues.ListComments(
          ctx,
          os.Getenv("GITHUB_OWNER"),
          os.Getenv("GITHUB_REPO"),
          githubPrNumber,
          opts)
    if err != nil {
        return nil, nil, err
    }
    return comments, response, nil
}

 

Как мне это заглушить?

Моя мысль состояла в том, чтобы, возможно, использовать инъекцию зависимостей, сначала создав свои собственные структуры, но я не уверен, как это сделать, поэтому в настоящее время у меня есть это:

 func TestGetClient(t *testing.T) {
    client, ctx, err := GetClient(os.Getenv("GITHUB_TOKEN"), "https://example.com/api/v3/")
    c, r, err := GetPRComments(ctx, client)
    ...
}
 

Комментарии:

1. github.com/migueleliasweb/go-github-mock

2. — Принимать интерфейсы, возвращать структуры. — Заменить client.Issues.ListComments на Issuer.ListComments (Эмитент-это интерфейс) — Макет ListComments метода для выполнения модульного теста.

Ответ №1:

Я бы начал с интерфейса:

 type ClientProvider interface {
  GetClient(token string, url string) (*github.Client, context.Context, error)
}
 

При тестировании устройства, которому необходимо позвонить GetClient , убедитесь, что вы зависите от своего ClientProvider интерфейса:

 func YourFunctionThatNeedsAClient(clientProvider ClientProvider) error {
  // build you token and url

  // get a github client
  client, ctx, err := clientProvider.GetClient(token, url)
  
  // do stuff with the client

  return nil
}
 

Теперь в своем тесте вы можете создать заглушку, подобную этой:

 // A mock/stub client provider, set the client func in your test to mock the behavior
type MockClientProvider struct {
  GetClientFunc func(string, string) (*github.Client, context.Context, error)
}

// This will establish for the compiler that MockClientProvider can be used as the interface you created
func (provider *MockClientProvider) GetClient(token string, url string) (*github.Client, context.Context, error) {
  return provider.GetClientFunc(token, url)
}

// Your unit test
func TestYourFunctionThatNeedsAClient(t *testing.T) {
  mockGetClientFunc := func(token string, url string) (*github.Client, context.Context, error) {
    // do your setup here
    return nil, nil, nil // return something better than this
  }

  mockClientProvider := amp;MockClientProvider{GetClientFunc: mockGetClientFunc}

  // Run your test
  err := YourFunctionThatNeedsAClient(mockClientProvider)

  // Assert your result
}
 

Эти идеи не мои собственные, я позаимствовал их у тех, кто был до меня; Мэт Райер предложил это (и другие идеи) в замечательном видео об «идиоматическом голанге».

Если вы хотите заглушить сам клиент github, можно использовать аналогичный подход, если github.Клиент-это структура, вы можете затенить ее с помощью интерфейса. Если это уже интерфейс, описанный выше подход работает напрямую.