c# - ¿Cómo sabe la biblioteca de afirmaciones Shouldly la expresión a la que se aplicó la aserción?
.net (2)
Lo hace analizando el seguimiento de la pila para la pila de llamadas actual hasta que encuentra un método que no es interno a Shouldly y trata su nombre como el nombre del método bajo prueba.
La biblioteca de aserción Shouldly para .NET de alguna manera sabe a qué expresión se invocó el método de aserción para que pueda mostrarla en el mensaje. Intenté averiguar cómo funciona, pero me perdí en el código fuente. Sospecho que se ve en el código compilado, pero realmente me gustaría ver cómo sucede esto. De la documentación
map.IndexOfValue("boo").ShouldBe(2); // -> map.IndexOfValue("boo") should be 2 but was 1
De alguna manera, Shouldly conoce la expresión map.IndexOfValue ("boo") y pudo mostrarla en el mensaje de error de prueba. ¿Alguien sabe cómo sucede esto?
Mirando el código, eso es bastante inteligente.
La magia está sucediendo en la clase ActualCodeTextGetter
. Primero, recupera la línea del archivo de código fuente usando 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;
Una vez que tiene el nombre del archivo de código fuente, junto con la línea y el desplazamiento de la declaración, es solo una cuestión de leer directamente el archivo:
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;
}
Hay mucha más lógica para aislar precisamente la afirmación, pero en resumen, el truco es:
- Utilice StackTrace para recuperar la ubicación del código fuente y la línea de la declaración
- Analizar el código fuente para recuperar la declaración exacta
Por supuesto, solo puede funcionar si lo está ejecutando en la misma máquina que utilizó para compilar el código.