haxetink / tink_unittest

Tinkerbell Unit Testing
https://haxetink.github.io/tink_unittest
16 stars 4 forks source link

Multiple asserts in the same test #3

Closed back2dos closed 7 years ago

back2dos commented 7 years ago

Basically I'd like the possibility to be doing something like:

causeSideEffectA();
assert(X);
causeSideEffectB();
assert(Y);

I'm not even sure how this fits with async tests or anything - just opening the issue to see what ideas we can come up with.

kevinresol commented 7 years ago

Just throwing some ideas

What about injecting some helper objects like so:

function myTest(helper) {
   causeSideEffectA();
   helper.assert(X);
   causeSideEffectB();
   helper.assert(Y);
   return helper.result;
}
back2dos commented 7 years ago

I think the following might work:

causeSideEffectA();
assert(X);
causeSideEffectB();
assert(Y);
done();

Becomes:

var assertions = new Accumulator();
causeSideEffectA();
assertions.yield(Step(new Assertion(X));
causeSideEffectB();
assertions.yield(Step(new Assertion(Y));
{
  return Promise.lift(
     if (assertions.ended) new Error("Attempted to end test twice")
     else {
       assertions.yield(End); 
       Noise;
     }
  )'
}

The thing is though that we have to somehow get the assertions into the test. One option would be to have them as a static variable outside the class. In that case we won't be able to run tests in parallel (which in some super distant future would be a cool feature). An alternative - the best one I can think of now - would be to give all tests a base class and then we can do anything we want per @:autoBuild and also add assert and done as instance macros on the base class. Autogenerated wrappers like TestSuiteBuilder are certainly more elegant than forcing people to use a base class. But the base class could complement TestSuiteBuilder by providing some features "from inside" for those who want them. After all, build macros are the most effective tool for bending syntax.

kevinresol commented 7 years ago

How about this?

function sync() {
  var buf = new AssertionBuffer();
  buf.assert(expected == value);
  buf.assert(expected == value);
  return buf.complete();
}

function async() {
  var buf = new AssertionBuffer();
  getPromise().handle(function() {
    buf.assert(expected == value);
    buf.assert(expected == value);
    buf.complete();
  });
  return buf;
}
back2dos commented 7 years ago

Something of that kind, yes. I'm after two things:

  1. Minimize noise - I don't really want to be creating an AssertionBuffer in every test.
  2. Have some level of flow control.

To explain the later point, the following test will hang:

function async() {
  var buf = new AssertionBuffer();
  getPromise().next(function() {
    buf.assert(expected == value);
    buf.assert(expected == value);
  });
  return buf;
}

I realize that my idea doesn't really address that either. Anyway, we don't need to rush things here. I think #4 helps cutting the number of assertions down.

kevinresol commented 7 years ago

I added AssertionBuffer and the usage is as follows:

    @:describe('Multiple assertions')
    public function multiAssert(asserts:AssertionBuffer) {
        var timer = new haxe.Timer(500);
        var i = 0;
        timer.run = function()
            if(i++ < 3) asserts.assert(i == i);
            else {
                asserts.done();
                timer.stop();
            }
        return asserts;
    }

I think it is the best thing we can do at this moment (without build macro)

back2dos commented 7 years ago

Actually, I realize now that flow control is actually not an issue. A test that doesn't return control cannot pass anyway ^^

I've thrown together a little autobuild macro for my needs. I'll make you a PR if it proves to be useful ;)