-
Notifications
You must be signed in to change notification settings - Fork 778
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Deprecate assert.expect? #666
Comments
Tracking call counts manually (even through a spy) seems really annoying if you actually care about this beyond the setup of a spy/mock object. I'm not sure how async tests avoid the need to define the expectation though. Consider the following: assert.expect( 2 );
var done = assert.async();
$( "<div>" ).dialog({
create: function() {
assert.ok( true, "Create was invoked" );
},
open: function() {
assert.ok( true, "Open was invoked" );
done();
}
}); Without the expectation, how do you know if create was called? You can certainly track it manually, but I'm not sure how making this type of testing more verbose improves anything. As for synchronous tests, here's another example: assert.expect( 4 );
var headers = element.find( ".foo-headers" );
headers.each(function() {
assert.equal( this.whatever, ... );
}); The above implicitly expects 4 headers to be found. The verification is done through the expectation. This can be rewritten as: var headers = element.find( ".foo-headers" );
assert.equal( headers.length, 4, "Correct number of headers found" );
headers.each(...); |
I would probably rewrite the first example using JamesMGreene/qunit-assert-step: var done = assert.async();
$( "<div>" ).dialog({
create: function() {
assert.step( 1, "Create was invoked" );
},
open: function() {
assert.step( 2, "Open was invoked" );
done();
}
}); For the latter example, I think its second implementation is far superior because it is both semantically correct and explicit about the details of its assertions. The implicit form has bitten me many times before, particularly if I have an existing looping assertion + an |
I agree with @JamesMGreene about preferring the explicit assertions, whether through qunit-assert-step or spies or even simple manual counters. In the end, though, |
Another thought: exposing var done = assert.async(),
initialCount = assert.count();
$( "<div>" ).dialog({
create: function() {
assert.ok( true, "Create was invoked" );
},
open: function() {
assert.ok( true, "Open was invoked" );
assert.equal( assert.count() - initialCount, 2, "Correct assertion count" );
done();
}
}); |
I agree that something like |
@jzaefferer @Krinkle @leobalter: Weigh in? |
I know this pattern is common, but in my opinion this is a poor quality assertion. Aside from following asynchronous flow, there should never be assertions outside the main body of the test function. Execute source code, then make assertions; not nested like above. There's quite a few different approaches one could take that are more robust and correct from a my point of view. test('dialog - bool', function (assert) {
var created, opened;
$('<div>').dialog({
create: function () {
created = true;
},
open: function () {
opened = true;
}
});
assert.ok(created1, 'create');
assert.ok(opened, 'create');
}); The plain boolean assertion works, but is not very strict (doesn't care about execution order or whether callbacks are intentionally or accidentally invoked multiple times). test('dialog - count', function (assert) {
var created = 0, opened = 0;
$('<div>').dialog({
create: function () {
created++;
},
open: function () {
opened++;
}
});
assert.equal(created, 1, 'create');
assert.equal(opened, 1, 'create');
}); The counter approach is more strict (guards against unexpected executions), but is a bit verbose and tedious to maintain and setup. test('dialog - flow', function (assert) {
var flow = [];
$('<div>').dialog({
create: function () {
flow.push('create');
},
open: function () {
flow.push('open');
}
});
assert.deepEqual(
flow,
['create', 'open'],
'callback flow'
);
}); The flow approach is my personal favourite and recommendation. It doesn't feel like a workaround and has minimal overhead and boilerplate. It strictly asserts the execution order and will notice any unexpected executions. It scales easily when you add more complexity. |
There're now many points on this discussion
|
about the async example, I've remembering of this on our current tests and it should properly on the var done1 = assert.async();
var done2 = assert.async();
setTimeout(function() {
assert.ok( true, "test resumed from async operation 1" );
done1();
}, 500 );
setTimeout(function() {
assert.ok( true, "test resumed from async operation 2" );
done2();
}, 150); |
@Krinkle the "flow array" approach looks good for synchronous tests, but Scott's example was about asynchronous tests. How would you implement that? |
You'd do it with 2 |
That example doesn't check the order of execution. In Scott's example, if |
QUnit |
Removing an existing method in favour of adding a plugin for a common use case is not an improvement. |
Also, if we want to get technical, Scott's example does not guarantee verification of the order of execution. It only verifies that one of the following occurs:
|
We can leave |
Sounds like a bug in QUnit if that's true. Once the done callback is invoked, no assertions should be allowed to occur. |
That's never been a requirement to date, AFAIK. The behavior you describe is what I would expect from something like a theoretical If we want to make it a requirement, that's fine [and now is the time to do it!], but we'll have to document that behavior very explicitly as it will definitely a potential gotcha for existing QUnit users. |
Making Timo's example async: test('dialog - flow', function (assert) {
var flow = [],
done = assert.async();
$('<div>').dialog({
create: function () {
flow.push('create');
},
open: function () {
flow.push('open');
assert.deepEqual(
flow,
['create', 'open'],
'callback flow'
);
done();
}
});
}); This can be extended to any number of callbacks, though I'm not sure how valuable that kind of test really would be.
Indeed. I've filed that as a separate ticket, #668 |
We discussed this at the IRC meeting today. We're closing this, keeping |
@Krinkle expressed an opinion that, with the
assert.async()
addition, we no longer needassert.expect()
whatsoever. At first I was a bit skeptical of this statement as I have grown into the habit of always usingassert.expect()
but I later realized he was correct.For example, take this "synchronous callback" example from the Cookbook page:
While this setup can be effectively replaced with the
assert.async()
approach as well, I would argue that that is semantically invalid as the operation is intended to be synchronous (@Krinkle disagrees, FYI).However, there are still at least 2 other ways to easily test this without using
assert.expect
, as @Krinkle demonstrated in a gist:Simple call counter:
Sinon spies:
@jzaefferer @leobalter @gibson042 @scottgonzalez et al: Thoughts? Do you see any other use cases were
assert.expect
is important to keep around? Does it provide enough convenience over the two alternative approaches above that we should keep it around anyway?The text was updated successfully, but these errors were encountered: