You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在单元测试中为特定用例设置LaunchDarkly Flag值

问题描述

我正尝试对一个受LaunchDarkly Flag部分控制的函数进行单元测试,希望除一个测试用例外,其余所有用例的Flag值均设为true。为实现这一点,我尝试为该例外用例的上下文设置特定属性,以便LaunchDarkly evaluator能够识别,但始终找不到正确的上下文键、上下文类型(context kind)和属性配置方式。

以下是我的代码实现:

测试文件代码

import (
    "github.com/launchdarkly/go-server-sdk/v7/ldcomponents"
    "github.com/launchdarkly/go-server-sdk/v7/testhelpers/ldtestdata"
    ld "github.com/launchdarkly/go-server-sdk/v7"
    ofld "github.com/open-feature/go-sdk-contrib/providers/launchdarkly/pkg"
    "github.com/open-feature/go-sdk/openfeature"
)

func NewTestFlaggingClient() (*ldtestdata.TestDataSource, *ld.LDClient) {
    td := ldtestdata.DataSource()

    var ldConfig ld.Config
    ldConfig.ApplicationInfo.ApplicationID = "testing"
    ldConfig.DataSource = td
    ldConfig.Events = ldcomponents.NoEvents()

    ldClient, err := ld.MakeCustomClient("fake-sdk-key", ldConfig, 5*time.Second)
    if err != nil {
        return nil, err
    }

    if err := openfeature.SetProvider(ofld.NewProvider(ldClient)); err != nil {
        return nil, err
    }

    return td, ldClient
}

func Test_myMethod(t *testing.T) {
  // ...
  disabledCtx := openfeature.NewEvaluationContext("user",
    map[string]interface{}{
      "disable-flag": true
    })

  td, flagger := flags.NewTestFlaggingClient()
  
  // 我试图创建一个始终为true的Flag
  // 仅在遇到上述上下文时为false
  td.Update(td.Flag("method-enabled").
    BooleanFlag().
    VariationForKey("user", "disable-flag", false).
    FallthroughVariation(true),
  )

    t.Run("Skip if flag is not set", func(t *testing.T) {
      // 将disabledCtx添加到父上下文
      ctx := openfeature.WithTransactionContext(ctx, disabledCtx)

      err := myMethod(ctx)
      // ...
    })
}

目标函数代码

// myMethod implementation
func myMethod(ctx context.Context) error {
  // Flag评估始终会接收此匿名上下文及父上下文
  anonCtx := openfeature.NewEvaluationContext("anonymous", map[string]any{
    "kind":      "user",
    "anonymous": true,
  })

  if !p.clients.Flags.Boolean(ctx, "mm-return-processor-enabled", false, anonCtx) {
    return nil
  }
}

调试openfeature和launchdarkly包后仍未找到解决办法。


解决方案

核心问题在于混淆了OpenFeature上下文与LaunchDarkly测试数据源的规则匹配逻辑,同时myMethod中生成的匿名上下文会干扰父上下文属性传递,具体修正步骤如下:

1. 修正Flag规则的匹配逻辑

VariationForKey仅用于匹配特定用户ID,要基于上下文属性匹配,需使用IfMatch条件规则,同时确保Flag名称与业务代码中一致:

td.Update(td.Flag("mm-return-processor-enabled") // 与myMethod中调用的Flag名完全一致
    .BooleanFlag()
    .IfMatch("disable-flag", true) // 匹配上下文属性disable-flag=true的情况
    .ThenVariation(false)
    .FallthroughVariation(true), // 默认返回true
)

2. 修正上下文属性合并逻辑

myMethod中传入的anonCtx会作为评估的主上下文,父上下文属性不会自动继承,需手动合并:

func myMethod(ctx context.Context) error {
    // 从context中提取父OpenFeature上下文
    parentEvalCtx := openfeature.FromContext(ctx)
    parentAttrs := parentEvalCtx.Attributes()

    // 创建匿名上下文并合并父属性
    anonAttrs := map[string]any{
        "kind":      "user",
        "anonymous": true,
    }
    // 将父上下文的属性合并到匿名上下文中
    for k, v := range parentAttrs {
        anonAttrs[k] = v
    }
    anonCtx := openfeature.NewEvaluationContext("anonymous", anonAttrs)

    if !p.clients.Flags.Boolean(ctx, "mm-return-processor-enabled", false, anonCtx) {
        return nil
    }
    // 业务逻辑...
}

3. 修正测试用例的上下文传递

确保测试上下文被正确存入context.Context中:

func Test_myMethod(t *testing.T) {
    // 创建带禁用属性的测试上下文
    disabledAttrs := map[string]interface{}{"disable-flag": true}
    disabledEvalCtx := openfeature.NewEvaluationContext("test-user", disabledAttrs)

    td, _ := flags.NewTestFlaggingClient()
    // 配置Flag规则
    td.Update(td.Flag("mm-return-processor-enabled")
        .BooleanFlag()
        .IfMatch("disable-flag", true)
        .ThenVariation(false)
        .FallthroughVariation(true),
    )

    t.Run("Skip if flag is disabled", func(t *testing.T) {
        // 将OpenFeature上下文存入context.Context
        ctx := openfeature.NewContext(disabledEvalCtx)
        err := myMethod(ctx)
        // 断言:预期方法直接返回nil
        // assert.NoError(t, err)
    })

    t.Run("Execute full logic when flag is enabled", func(t *testing.T) {
        // 使用不带禁用属性的上下文
        enabledEvalCtx := openfeature.NewEvaluationContext("test-user-2", nil)
        ctx := openfeature.NewContext(enabledEvalCtx)
        err := myMethod(ctx)
        // 断言:预期方法执行完整业务逻辑
        // ...
    })
}

关键注意事项

  • Flag名称必须完全一致:测试中配置的Flag名要和业务代码中调用的mm-return-processor-enabled完全匹配,之前测试代码中用的method-enabled是核心错误之一。
  • OpenFeature上下文不会自动合并:当调用Boolean方法传入第二个上下文参数时,它会作为评估的主上下文,父上下文属性不会自动继承,必须手动合并。
  • 区分规则匹配方式:VariationForKey仅匹配用户ID,IfMatch用于匹配上下文属性,不要混淆两者用法。

内容的提问来源于stack exchange,提问作者Gabe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 14:47:36