News for hackers. Stuff that matters.
Re:Stop requiring only one assertion per unit test: Multiple... by torginus on Saturday November 05, 2022 @08:21AM (#33480756)
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 60
      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.

Re:Stop requiring only one assertion per unit test: Multiple... by superjan on Saturday November 05, 2022 @08:32AM (#33480804)
Is this shared somewhere?
Re:Stop requiring only one assertion per unit test: Multiple... by torginus on Saturday November 05, 2022 @08:49AM (#33480913)
Nah, it was some corporate project. But if you are interested, I could rewrite it. It would be a fun weekend project.
Re:Stop requiring only one assertion per unit test: Multiple... by mattmanser on Saturday November 05, 2022 @09:27AM (#33481252)
It'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().

Re:Stop requiring only one assertion per unit test: Multiple... by mananaysiempre on Saturday November 05, 2022 @09:30AM (#33481282)
FWIW, in Python, this is pytest’s entire thing (although it ends up grovelling into bytecode to achieve that).
Re:Stop requiring only one assertion per unit test: Multiple... by throwaway6977 on Saturday November 05, 2022 @09:34AM (#33481311)
Not 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(...)"

Re:Stop requiring only one assertion per unit test: Multiple... by opminion on Saturday November 05, 2022 @10:00AM (#33481546)
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()

Re:Stop requiring only one assertion per unit test: Multiple... by nerdponx on Saturday November 05, 2022 @10:07AM (#33481598)
This 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.
Re:Stop requiring only one assertion per unit test: Multiple... by beezlewax on Saturday November 05, 2022 @10:19AM (#33481715)
Is there something like this available for javascript?
Re:Stop requiring only one assertion per unit test: Multiple... by tobyhinloopen on Saturday November 05, 2022 @10:40AM (#33481907)
Not 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.

Re:Stop requiring only one assertion per unit test: Multiple... by fiddlerwoaroof on Saturday November 05, 2022 @12:43PM (#33483107)
Well, 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.
Re:Stop requiring only one assertion per unit test: Multiple... by tobyhinloopen on Sunday November 06, 2022 @04:31PM (#33497447)
Can 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.

Re:Stop requiring only one assertion per unit test: Multiple... by fiddlerwoaroof on Sunday November 06, 2022 @04:40PM (#33497555)
You 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.
Re:Stop requiring only one assertion per unit test: Multiple... by xeromal on Saturday November 05, 2022 @11:06AM (#33482174)
I'm not aware of any related to testing, but perhaps you could use something like this in tandem with some tests to pull it off?

https://github.com/gvergnaud/ts-pattern

Re:Stop requiring only one assertion per unit test: Multiple... by eurasiantiger on Saturday November 05, 2022 @12:03PM (#33482727)
You could parse the failing expression using babel and get an AST back.
Re:Stop requiring only one assertion per unit test: Multiple... by dalmo3 on Saturday November 05, 2022 @06:33PM (#33486623)
It might not be the general solution you're looking for, but idiomatic Jest does the job for me.

   expect(width).toBeGreaterThan(0); expect(x+width).toBeLessThan(screenWidth);
When an assertion fails it will tell you: "Expected: X; Received: Y"
Re:Stop requiring only one assertion per unit test: Multiple... by fomine3 on Monday November 07, 2022 @12:59AM (#33501745)
power-assert
Re:Stop requiring only one assertion per unit test: Multiple... by jmount on Saturday November 05, 2022 @10:50AM (#33482008)
Nice framework. Also once you allow more than one assertion, there is no need for top-level && in assertions (making them simpler tests).
Re:Stop requiring only one assertion per unit test: Multiple... by torginus on Saturday November 05, 2022 @10:53AM (#33482036)
While there is no strict need, sometimes assertions logically belong together.
Re:Stop requiring only one assertion per unit test: Multiple... by erik_seaberg on Saturday November 05, 2022 @04:48PM (#33485530)
You definitely want x || y, x ^^ y, and x implies y.
Re:Stop requiring only one assertion per unit test: Multiple... by paphillips on Saturday November 05, 2022 @12:18PM (#33482887)
Related 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-ref... [2] https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...

Re:Stop requiring only one assertion per unit test: Multiple... by sasmithjr on Saturday November 05, 2022 @12:19PM (#33482891)
I 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