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

我怕爱的太早我们不能终老 提交于 2020-12-08 05:29:11

问题


The Shouldly assertion library for .NET somehow knows what expression the assertion method was called on so it is able to display it into the message. I tried to find out how it works but got lost in the source code. I suspect it looks into the compiled code but I would really like to see how this happens. From the documentation

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

Somehow Shouldly knows the expression map.IndexOfValue("boo") and was able to display it in the test failure message. Does anyone know how this happens?


回答1:


Looking at the code, that's pretty smart.

The magic is happening in the ActualCodeTextGetter class. First, it retrieves the line of the source code file by using the 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;

Once it has the name of the source code file, along with the line and offset of the statement, it's just a matter of reading directly the file:

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;
}

There's a lot more logic going on to isolate precisely the statement, but in short the trick is:

  • Use the StackTrace to retrieve the location of the source code and the line of the statement
  • Parse the source code to retrieve the exact statement

Of course, it can work only if you're running it on the same machine you used to compile the code.




回答2:


It does it by parsing the stack trace for the current call stack until it finds a method that is not internal to Shouldly and treats its name as the name of the method under test.



来源:https://stackoverflow.com/questions/39959272/how-does-the-shouldly-assertion-library-know-the-expression-the-assertion-was-ap

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!