Shouldly 断言库如何知道应用断言的表达式?

How does the Shouldly assertion library know the expression the assertion was applied to?

Shouldly assertion library for .NET 以某种方式知道调用断言方法的表达式,因此它能够将其显示到消息中。我试图找出它是如何工作的,但在源代码中迷路了。我怀疑它会查看编译后的代码,但我真的很想看看这是怎么发生的。来自文档

map.IndexOfValue("boo").ShouldBe(2); // -> map.IndexOfValue("boo") should be 2 but was 1

Shouldly 知道表达式 map.IndexOfValue("boo") 并且能够在测试失败消息中显示它。有谁知道这是怎么发生的?

看代码,很聪明。

魔法正在 ActualCodeTextGetter class 中发生。首先,它使用 StackTrace 检索源代码文件的行:

  StackTrace stackTrace = trace ?? new StackTrace(true);

  // Cut for brevity

  StackFrame stackFrame = frame;
  this.ShouldlyFrameIndex = index - 1;
  string fileName = stackFrame.GetFileName();
  this._determinedOriginatingFrame = fileName != null && File.Exists(fileName);
  this._shouldMethod = this.ShouldlyFrame.GetMethod().Name;
  this.FileName = fileName;
  this.LineNumber = stackFrame.GetFileLineNumber() - 1;

一旦有了源代码文件的名称,以及语句的行和偏移量,直接读取文件就可以了:

private string GetCodePart()
{
  string str = "Shouldly uses your source code to generate its great error messages, build your test project with full debug information to get better error messages\nThe provided expression";
  if (this._determinedOriginatingFrame)
  {
    string codeLines = string.Join("\n", ((IEnumerable<string>) File.ReadAllLines(this.FileName)).Skip<string>(this.LineNumber).ToArray<string>());
    int indexOfMethod = codeLines.IndexOf(this._shouldMethod);
    if (indexOfMethod > 0)
      str = codeLines.Substring(0, indexOfMethod - 1).Trim();
    str = !str.EndsWith("Should") ? str.RemoveVariableAssignment().RemoveBlock() : this.GetCodePartFromParameter(indexOfMethod, codeLines, str);
  }
  return str;
}

要精确地分离语句还有很多逻辑,但简而言之,诀窍是:

  • 使用 StackTrace 检索源代码的位置和语句的行
  • 解析源代码以检索准确的语句

当然,只有当你运行它在你用来编译代码的同一台机器上时它才能工作。

它通过 parsing the stack trace for the current call stack 进行,直到找到一个不属于 Shouldly 内部的方法,并将其名称视为被测方法的名称。