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

如何为FluentAssertions内置断言的{context}追加路径内容?

解决方案

要给FluentAssertions的内置断言追加{context}内容,核心是在自定义断言中继承并拼接父上下文,而不是直接覆盖它。你之前的尝试失败,主要是因为没有正确获取上层上下文,或者错误地覆盖了原有上下文值。

核心思路

  1. 在自定义断言方法中,先获取当前AssertionScope的已有上下文(如果存在),再拼接上当前层级的子路径,生成新的完整上下文。
  2. 使用这个新上下文创建AssertionScope,在作用域内执行后续断言——这样内置断言会自动继承并使用拼接后的完整上下文路径。

修正后的完整代码

示例类

public class Foo { public Bar Bar { get; } = new Bar(); }
public class Bar { public bool IsFull {get; set; } }

扩展类(修正后)

public static class Extensions
{
    public static FooAssertions Should(this Foo foo) => new FooAssertions(foo);
    public static BarAssertions Should(this Bar bar) => new BarAssertions(bar);

    public class FooAssertions
    {
        public Foo Subject { get; }

        public FooAssertions(Foo subject)
        {
            Subject = subject;
        }

        [CustomAssertion]
        public void HaveFullBar(string because = null, params object[] becauseArgs)
        {
            // 拼接上层上下文(初始为"foo")与当前子路径".Bar"
            var newContext = $"{AssertionScope.Current.Context ?? nameof(Foo)}.{nameof(Foo.Bar)}";
            using (var scope = new AssertionScope(newContext))
            {
                Subject.Bar.Should().BeFull(because, becauseArgs);
            }
        }
    }

    public class BarAssertions
    {
        public Bar Subject { get; }

        public BarAssertions(Bar subject)
        {
            Subject = subject;
        }

        [CustomAssertion]
        public void BeFull(string because = null, params object[] becauseArgs)
        {
            // 拼接上层上下文("foo.Bar")与当前子路径".IsFull"
            var newContext = $"{AssertionScope.Current.Context ?? nameof(Bar)}.{nameof(Bar.IsFull)}";
            using (var scope = new AssertionScope(newContext))
            {
                Subject.IsFull.Should().BeTrue(because, becauseArgs);
            }
        }
    }
}

测试方法

[TestMethod]
public void CustomTest()
{
    var foo = new Foo();
    foo.Should().HaveFullBar();
}

最终输出结果

Expected foo.Bar.IsFull to be True but found False.

为什么之前的尝试失败?

  • 尝试一:直接用new AssertionScope(".Bar")覆盖了原有上下文,没有继承上层的"foo",导致最终只显示最内层的.IsFull。
  • 尝试二:AssertionScope构造函数不支持解析{context}占位符,所以直接输出了未替换的{context}.IsFull。
  • 尝试三:初始的AssertionScope.Current.Context为null,是因为HaveFullBar执行初期,FluentAssertions还未完全初始化上下文。我们通过??运算符设置默认值(比如nameof(Foo)),就能确保即使上下文未初始化也能正确拼接路径。

更简洁的封装写法

如果你不想每次都手动拼接上下文,可以封装一个辅助方法来自动处理:

private static AssertionScope CreateScopedContext(string subPath)
{
    var currentContext = AssertionScope.Current.Context ?? string.Empty;
    var newContext = string.IsNullOrEmpty(currentContext) 
        ? subPath 
        : $"{currentContext}.{subPath}";
    return new AssertionScope(newContext);
}

然后在自定义断言中直接调用:

[CustomAssertion]
public void HaveFullBar(string because = null, params object[] becauseArgs)
{
    using (var scope = CreateScopedContext(nameof(Foo.Bar)))
    {
        Subject.Bar.Should().BeFull(because, becauseArgs);
    }
}

这样就能更优雅地管理上下文的追加逻辑了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:51:22