Как библиотека Shouldly assertion знает выражение, к которому было применено утверждение?

Библиотека Shouldly assertion для .NET каким-то образом знает, на какое выражение вызывается метод assert, чтобы он мог отображать его в сообщении. Я попытался выяснить, как это работает, но потерялся в исходном коде. Я подозреваю, что он просматривает скомпилированный код, но мне очень хотелось бы посмотреть, как это происходит. Из документации

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

Как-то Должно знать выражение map.IndexOfValue( "boo" ) и смог отобразить его в сообщении об ошибке тестирования. Кто-нибудь знает, как это происходит?

Ответы

Ответ 1

Глядя на код, это довольно умный.

Магия происходит в классе ActualCodeTextGetter. Во-первых, он извлекает строку файла исходного кода с помощью 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 для извлечения местоположения исходного кода и строки оператора
  • Разберите исходный код для получения точной инструкции

Конечно, он может работать только в том случае, если вы запускаете его на том же компьютере, который использовался для компиляции кода.