6 ms·
I remember writing a small .NET test library for that exact problem - You could pass in a lambda with a complex condition, and it evaluated every piece of the e
by torginus 4y ago
I remember writing a small .NET test library for that exact problem - You could pass in a lambda with a complex condition, and it evaluated every piece of the expression separately and pretty printed what part of the condition failed.
So essentially you could write
Assert(()=>width>0 && x + width < screenWidth)
And you would get:
Assertion failed:
x is 1500
width is 600
screenWidth is 1920
It used Expression<T> to do the magic. Amazing debug messages. No moralizing required.
This was a huge boon for us as it was a legacy codebase and we ran tens of thousands of automated tests and it was really difficult to figure out why they failed.
- superjan 4y agoIs this shared somewhere?
- torginus 4y agoNah, it was some corporate project. But if you are interested, I could rewrite it. It would be a fun weekend project.
- mattmanser 4y agoIt's also worth figuring out for yourself though! They're suprisingly easy to use (look up Expression<>). It's incredibly useful once you know how, and encourages you to stop using reflection, passing magic strings as arguments, or having to use nameof().
- mananaysiempre 4y agoFWIW, in Python, this is pytest’s entire thing (although it ends up grovelling into bytecode to achieve it).
- throwaway6977 4y agoNot quite the same, but available on nuget- 'fluentAssertions' gives you something akin to this. I've had decent success with having our juniors use it vs less verbose assertion libraries. I don't know about evaluating individual expressions in a line separately, but it does give you clean syntax and similar error messages that are very readable- "GetProductPage().GetProductPrice().Should().Be().LessThan(...)"
- opminion 4y agohttps://stackoverflow.com/a/700325 https://stackoverflow.com/a/700325 to write out the internal structure of a LambdaExpression. If you just want the assignments then it's simpler: Add to the Evaluate method a test for MemberExpression and then: The variable name is: ((MemberExpression)expr).Member.Name The value is: Expression.Lambda(expr).Compile().DynamicInvoke()
- nerdponx 4y agoThis is what the Python test framework Pytest does, among many other similar useful and magical things. I believe that the Python developer ecosystem as a whole would be substantially less productive without it.
- beezlewax 4y agoIs there something like this available for javascript?
- tobyhinloopen 4y agoNot really, no. I like to use Jest’s toMatchObject to combine multiple assertions in a single assertion. If the assertion fails, the full object on both sides is shown in logs. You can easily debug tests that way. The only way to make it even possible is to do some eval magic or to use a pre-processor like babel or a typescript compiler plugin. But if you find something, Lemme know.
- fiddlerwoaroof 4y agoWell, Function.toString() should print the code of a lambda in JavaScript. So I think you could do it without a pre-processor: use Babel as a library to parse the body of the function; run each sub-expression separately and display the results.
- tobyhinloopen 4y agoCan you actually do that, since you don’t have the context the function is running in? You can inspect the source, but functions don’t run in isolation so you cannot extract the source and run it somewhere else. Simple example: var x = 0; var y = function() { x++; }; You can’t reliably execute function y with just y’s function body. Maybe if you replace the function with another by injecting code into a copy of the source, but I’m not sure if that’s even possible.
- fiddlerwoaroof 4y agoYou can just document a constraint that these function must use the purely functional subset of JavaScript, which is enough for the sorts of assertions I typically write. Alternatively, you could constrain it to the subset that has no unbound variables or side effects.
- 4y ago
- jmount 4y agoNice framework. Also once you allow more than one assertion, there is no need for top-level && in assertions (making them simpler tests).
- torginus 4y agoWhile there is no strict need, sometimes assertions logically belong together.
- erik_seaberg 4y agoYou definitely want x || y, x ^^ y, and x implies y.
- paphillips 4y agoRelated to this, for anyone not fully up to date on recent C# features there is also the CallerArgumentExpression [1], [2] feature introduced in C# 10. While it is not a pretty printer for an expression, it does allow the full expression passed from the call site as an argument value to be captured and used within the method. This can be useful for custom assert extensions. For example: public void CheckIsTrue(bool value, [CallerArgumentExpression("value")] string? expression = null) { if (!value) { Debug.WriteLine($"Failed: '{expression}'"); } } So if you call like this: CheckIsTrue(foo != bar && baz == true), when the value is false it prints "Failed: 'foo != bar && baz == true'". [1] https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/caller-argument-expression https://learn.microsoft.com/en-us/dotnet/csharp/language-ref... [2] https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/attributes/caller-information https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...
- sasmithjr 4y agoI love using Unquote[0] in F# for similar reasons; it uses F#'s code quotations. Assuming the variables have been defined with the values you state, the assertion is written as: test <@ width > 0 && x + width < screenWidth @> And part of the output is: width > 0 && x + width < screenWidth 500 > 0 && x + width < screenWidth true && x + width < screenWidth true && 1500 + 500 < 1920 true && 2000 < 1920 true && false false [0]: https://github.com/SwensenSoftware/unquote https://github.com/SwensenSoftware/unquote