如何为FluentAssertions内置断言的{context}追加路径内容?
解决方案
要给FluentAssertions的内置断言追加{context}内容,核心是在自定义断言中继承并拼接父上下文,而不是直接覆盖它。你之前的尝试失败,主要是因为没有正确获取上层上下文,或者错误地覆盖了原有上下文值。
核心思路
- 在自定义断言方法中,先获取当前
AssertionScope的已有上下文(如果存在),再拼接上当前层级的子路径,生成新的完整上下文。 - 使用这个新上下文创建
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
相关产品推荐
相关产品推荐

