Since start(), stop() will be removed in Qunit 2.0, what is the alternative for async setups and teardowns via the beforeEach, afterEach methods? For instance, if I want the beforeEach to wait for a promise to be finished?
QUnit basically wants people to stop using the global methods (not just start() and stop(), but also test(), expect(), etc). So, as of version 1.16.0, you should always use either the global namespace (QUnit) or the assert API argument passed into the test() functions. This includes the new async control:
QUnit.test( "testing async action", function( assert ) { // <-- note the `assert` argument here
var done = assert.async(); // tell QUnit we're doing async actions and
// hold onto the function it returns for later
setTimeout(function() { // do some async stuff
assert.ok( true, "This happened 100 ms later!" );
done(); // using the function returned from `assert.async()` we
// tell QUnit we're don with async actions
}, 100);
});
If you are familiar with the old start() and stop() way of doing things, you should see that this is extremely similar, but more compartmentalized and extensible.
Because the async() method call is on the assert argument into the test, it cannot be used in the beforeEach() function. If you have an example of how you were doing that before, please post it and we can try to figure out how to git it into the new way.
UPDATE
My mistake previously, the assert object is being passed into the beforeEach and afterEach callbacks on modules, so you should be able to do the same logic that you would do for a test:
QUnit.module('set of tests', {
beforeEach: function(assert) {
var done = assert.async();
doSomethingAsync(function() {
done(); // tell QUnit you're good to go.
});
}
});
(tested in QUnit 1.17.1)
Seeing that nobody has answered the beforeEach/afterEach part: a test suite is supposed to run as soon as the page loads. When that is not immediately possible, then resort to configuring QUnit:
QUnit.config.autostart = false;
and continue with setting up your test suite (initializing tests, feeding them to QUnit, asynchronously waiting for some components to load, be it AJAX or anything else), your site, and finally, when it's ready, just run:
QUnit.start();
QUnit's docsite has it covered.
Ember Qunit, has once exists beforeEach/setup, afterEach/teardown co-exist for a little while.
See PR: https://github.com/emberjs/ember-qunit/pull/125
Related
I find myself writing this at the start of pretty much all of my unit tests in mocha:
it('should do something', (done) => {
Vue.config.errorHandler = done;
// do something aynchronous
});
By default, Vue catches all errors itself and logs them to the console, so mocha can't see them. This code makes sure that thrown errors fail the tests.
Is there a way with mocha to do this without having to start every single async test with this line of code? If I have to write / use a plugin, that's fine.
Try:
Vue.config.errorHandler = function (err, vm, info) {
throw err
}
in your test entry.
I can't seem to get the before Hook to be asynchronous.
I've used a before hook to start it up, which it does, but I'm trying to pick up when it fails, so that I can fail/skip the whole block (since there is no server to test with).
I've used the [async] before hook all throughout my code, but in this case it's not waiting for my done to be fired.
Basically, got a bunch of MochaJS tests, and for one block I need to start up a Web Server so that I can make Requests to it to test the RESTful API. I want to make sure that it started successfully, hence the async method.
describe('RESTful Tests', function(){
// Start up the whole express server for these API tests
before(function(done) {
try {
server.on('error', (e) => {
console.error("HERE");
done();
});
listener = server.listen(port, function() {
console.log("Server Started");
done();
});
console.error("AFTER LISTEN");
} catch(err) {
console.error("ON Caught");
done();
}
console.error("At End!!");
});
This runs, and shows as an 1) "before all" hook in the test, but it does not wait for the done to be called. I'm only getting the output...
RESTful Tests
AFTER LISTEN
At End!!
1) "before all" hook
None of the other items show up, but I'll get an exception (expected as I've blocked the port) about the EADDRINUSE. I'm really struggling to find a way to catch this.
I understand that it's in a different (kinda) "thread", so I wouldn't pick up the error, but still.... why is the asynchronous done method not being obeyed?
Seems some of the documentation out there is a bit misleading...
The Node/ExpressJS documentation state to put the .on('error' method on the server object. Seems in this case it needed to be on the listener object!
So my final [working] code looks like this...
listener = server.listen(port, function() {
done();
}).on('error', (function(e) {
this.skip(e.code);
done();
}).bind(this));
Yay!!! :)
Struggling with acceptance tests. Started with basic login test:
import { test } from 'qunit';
import moduleForAcceptance from 'static/tests/helpers/module-for-acceptance';
moduleForAcceptance('Acceptance | authentication');
test('login', function(assert) {
visit('/');
click('.nav-bar__login-link');
andThen(function() {
assert.notOk(find('.login-form__submit-button').attr('disabled'));
});
fillIn('.login-form__email-block input', "ruz#email.com");
fillIn('.login-form__password-block input', "qwe");
click('.login-form__submit-button');
andThen(function() {
console.log("ftw");
assert.equal(find('.nav-bar__profile-link').text(), "some");
});
});
The problem is that andThen callback is called before authentication completes. It's jQuery ajax request and a few promises after. From what I can see ember waits for ajax query to complete, but doesn't wait for promises to get resolved/rejected. Should this test work out of the box? Do I have to write a custom waiter?
It sounds like your promises may not be setup right? But no, you should be able to write tests using the acceptance test helpers and not need to worry about async calls settling (or promises resolving) yourself
My app requires users to sign in by submitting a form. I wonder what is the best place to do it in my tests. I came up with some options:
sign-in in beforeEach block (and signout in afterEach block)
sign-in in beforeAll block of every describe (and signout in the last afterAll of every describe)
describe('APP', function () {
describe('FEATURE 1', function () {
beforeAll(function () {
//sign in
});
afterAll(function () {
//sign out
});
//...
});
});
sign-in once for the whole test run in beforeAll of main describe
describe('MY APP', function () {
beforeAll(function () {
//sign in
});
describe('my feature 1', function () {
//...
});
});
Number 1 is the slowest, Number 2 is faster and Number 3 is the fastest, but you are required to have a single entry point for your test runner - not ideal. So which do think is better and why?
It's a matter of different things.
The best practice is closing your browser after EACH test case and it will definitely be the slowest way to run your test. But you will achieve the most clear and the most fair tests. Sure, you can use selenium grid and paralleling your tests and this will be definitely not so long as you expect.
Sometimes you have an application, which logic is not so much dependent on each other and you will probably want to run UI tests into the pre-commit hook for not breaking anything into the branch. Then the approach n.3 will be not so bad (the only thing you should know is what cookies, session vars and other artifacts you should delete in browser after each test).
Anyway that's up to you, but a common and the really BEST practice is opening a clear new browser on every test.
I try to create e2e tests with karma and jasmine with yeoman. In my karma-e2e.conf.js I add jasmine:
files = [
JASMINE,
JASMINE_ADAPTER,
ANGULAR_SCENARIO,
ANGULAR_SCENARIO_ADAPTER,
'test/e2e/**/*.js'
];
A need async testing so I need to use runs, waits, waitsFor (https://github.com/pivotal/jasmine/wiki/Asynchronous-specs)
But if I try to use it:
it('test', function () {
runs(function () {
...
});
});
Scenatio test runner returns this:
TypeError: Cannot call method 'runs' of null
at runs (http://localhost:8080/adapter/lib/jasmine.js:562:32)
at Object.<anonymous> (http://localhost:8080/base/test/e2e/eduUser.js:42:3)
at Object.angular.scenario.SpecRunner.run (http://localhost:8080/adapter/lib/angular-scenario.js:27057:15)
at Object.run (http://localhost:8080/adapter/lib/angular-scenario.js:10169:18)
I don't know where the problem is. Can you help me please?
Angular e2e tests with Karma don't and can't use the JASMINE adapter. Instead you have the ANGULAR_SCENARIO_ADAPTER which has a similar feel to writing Jasmine tests.
All commands in the adapter's API are asynchronous anyway. For example element('#nav-items').count() doesn't return a number, it returns a Future object. Future objects are placed in a queue and executed asynchronously as the runner progresses. To quote the API docs:
expect(future).{matcher}:
[...] All API statements return a future object, which get a value assigned after they are executed.
If you need to run your own asynchronous test code, you can extend the adapter's DSL, this is easier than it might sound. The idea is that you return your own Future which can be evaluated by a matcher such as toBe(). There are some examples on how to do this in the e2e-tests.js Gist from Vojta. Just remember to call done(null, myRetrunValue); when your test code is successful (myRetrunValue is the value evaluated by your matcher). Or done('Your own error message'); if you want the test to fail.
UPDATE: In response to question below. To simulate a login, first add a function called login to the dsl:
angular.scenario.dsl('login', function() {
return function(selector) {
// #param {DOMWindow} appWindow The window object of the iframe (the application)
// #param {jQuery} $document jQuery wrapped document of the application
// #param {function(error, value)} done Callback that should be called when done
// (will basically call the next item in the queuue)
return this.addFutureAction('Logging in', function(appWindow, $document, done) {
// You can do normal jQuery/jqLite stuff here on $document, just call done() when your asynchronous tasks have completed
// Create some kind of listener to handle when your login is complete
$document.one('loginComplete', function(e){
done(null, true);
}).one('loginError', function(e){
done('Login error', false);
});
// Simulate the button click
var loginButton = $document.find(selector || 'button.login');
loginButton.click();
})
};
});
And then call:
beforeEach( function()
{
expect( login('button.login') ).toBeTruthy();
});