首页 > 解决方案 > 如何将模拟服务器的 URL 注入 terraform 的验收测试?

问题描述

官方网站上有一整节关于 terraform 提供者的验收测试,假设我将在真实基础设施上使用测试帐户运行它。

也就是说,在我的情况下,我不能使用我的真实基础设施,并且计划使用模拟服务器而不是我想让 tf 提供者/客户端与之交谈,并且我确实有一个特定的变量:

provider "foo" {
  endpoint = var.endpoint
}

variable "endpoint" {
  type = string
  description = "Endpoint to talk to"
  default = "https://realinfra.com" % want to use "localhost:9090" (mock server's address) in tests instead
}

我可以在验收测试中以某种方式设置/通过它吗?这是我的测试的最小工作示例

package example

// example.Widget represents a concrete Go type that represents an API resource
func TestAccExampleWidget_basic(t *testing.T) {
    var widgetBefore, widgetAfter example.Widget
    rName := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)

    resource.Test(t, resource.TestCase{
        PreCheck:     func() { testAccPreCheck(t) },
        Providers:    testAccProviders,
        CheckDestroy: testAccCheckExampleResourceDestroy,
        Steps: []resource.TestStep{
            {
                Config: testAccExampleResource(rName),
                Check: resource.ComposeTestCheckFunc(
                    testAccCheckExampleResourceExists("example_widget.foo", &widgetBefore),
                ),
            },
            {
                Config: testAccExampleResource_removedPolicy(rName),
                Check: resource.ComposeTestCheckFunc(
                    testAccCheckExampleResourceExists("example_widget.foo", &widgetAfter),
                ),
            },
        },
    })
}

换句话说(或者像一个更一般的问题),你将如何在 tf provider 中设置变量的值,因为你有:

func Provider() *schema.Provider {
    return &schema.Provider{
        Schema: map[string]*schema.Schema{
        ...,
        "endpoint": {
                Type:     schema.TypeString,
                Optional: true,
                Default:  "https://realinfra.com",
            },
        "email": {
                Type:        schema.TypeString,
                Optional:    true,
                DefaultFunc: schema.EnvDefaultFunc("FOO_USERNAME", ""),
            }, 

标签: goterraformacceptance-testing

解决方案


有几种方法可以做到这一点:

1.环境变量

允许通过 env 变量设置提供者端点:

"endpoint": {
    Type:     schema.TypeString,
    Optional: true,
    DefaultFunc: schema.EnvDefaultFunc("FOO_ENDPOINT", "https://realinfra.com")
},

并要求将此环境变量设置为testAccPreCheck

func testAccPreCheck(t *testing.T) {
  if v := os.Getenv("FOO_ENDPOINT"); v == "" {
    t.Fatal("FOO_ENDPOINT env must be set for acceptance tests")
  }
}

然后你可以在你的 Makefile 中导出这个 env var

2.在测试用例中提供配置

您可以在测试用例配置中设置您的提供者配置:

// define mock address
const mockServer = "http://localhost:9090"

func TestAccExampleWidget_basic(t *testing.T) {
    ...

    resource.Test(t, resource.TestCase{
        ...
        Steps: []resource.TestStep{
            {
                // pass mock server to configs
                Config: testAccExampleResource(rName, mockServer),
                ...
            },
        },
    })
}

func testAccExampleResource(resourceName, server string) string {
    return fmt.Sprintf(`
# use mock serer address to configure your provider inside test case config
provider "myprovider" {
  endpoint = "%s"
}

myresource "myprovider_something" "foo" {
  name = "%s"
}`, server, resourceName)
}

请注意,您需要为每个测试配置执行此操作。

3. 构建mock API客户端并传递给测试提供者

允许将 api 客户端传递给提供者构造函数:
provider.go

func New(client *myApiClient) *schema.Provider {
    provider := &schema.Provider{...}

    provider.ConfigureContextFunc = func(_ context.Context, data *schema.ResourceData) (interface{}, diag.Diagnostics) {
        // return mock client if provided
        if client != nil {
            return client, nil
        }
       
        // existing client config here
        ...
        return client, nil
    }
}

使用空客户端更新提供程序入口点:
main.go

func main() {
    plugin.Serve(&plugin.ServeOpts{
        ProviderFunc: func() *schema.Provider {
            return provider.New(nil)
        },
    })
}

根据您的 API 客户端的初始化方式,创建模拟并将其传递给测试内部的提供者:
provider_test.go

var testAccProviderFactories map[string]func() (*schema.Provider, error)
var testAccProvider *schema.Provider
var client *myApiClient

func init() {
    client = &myApiClient{Endpoint: "http://localhost:9000"}
    testAccProvider = New(client)
    testAccProviderFactories = map[string]func() (*schema.Provider, error){
        "myprovider": func() (*schema.Provider, error) {
            return testAccProvider, nil
        },
    }
}

这种方法的灵感来自于keycloak 提供者测试


推荐阅读