I'm building some API tests for the product I'm building. One of the test looks like this:
GET FILTERS
✓ should be restricted (45ms)
it should get the filters
✓ should return 200
✓ should return an object
✓ should close db connections
GET USERS COUNT
✓ should be restricted
✓ should throw error when payload is not correct
it should get the user count
✓ should return 200
✓ should return an object
✓ should close db connections
GET USERS FILE
✓ should be restricted
✓ should throw error when no queryId is specified
it should retrieve the file
✓ should return 200
✓ should download an excel file
✓ should close db connections
UPLOAD PROMOTION IMAGE
✓ should throw error when no file is specified
it should save the file
✓ should return 200
✓ should have named the file with the title of the promotion
✓ should have uploaded the file to S3 (355ms)
✓ should close db connections
CREATE PROMOTION
it should save the promotion
✓ should return 200
✓ should return a correct response
✓ should close db connections
GET PROMOTIONS
✓ should be restricted
it should get the promotions
✓ should return 200
✓ should be an array of promotions
✓ should contain the previously created promotion
UPDATE PROMOTION
it should update the promotion
✓ should return 200
✓ should return a correct response
✓ should close db connections
PUT PROMOTION IN BIN
it should put the promotion in the bin
✓ should return 200
✓ should return a correct response
✓ should close db connections
GET ARCHIVED PROMOTIONS
✓ should be restricted
it should get the promotions
✓ should return 200
✓ should be an array of promotions
✓ should be an array of archived promotions
✓ should contain the previously archived promotion
DELETE PROMOTION
it should delete the promotion
✓ should return 200
✓ should return a correct response
✓ should have deleted the file from S3 (563ms)
✓ should close db connections
As you can see I've tried to put everything regarding promotions in a single test suite so that I can have a sort of workflow to test what the user will do exactly on the platform.
In this example I create a random generate promotion and then use the id of that promotion to read it, update it, archive it and then finally delete it. Every step is connected and I need to have return values for each suit (i.e: The ID of the inserted promotion, or the filters...)
At this moment my promotions.test.js file is 625 and, since I have not finished yet, I expect it to grow a lot in the next days.
Is there a way to split multiple test suits in different files but in a way that each test/file is able to return, as soon as it finishes, a value that I can pass to the next step ?
EDIT FOR BOUNTY
At the moment I only tried something like this:
describe.only("Gifts Workflow", function() {
var createdGift;
describe("CREATE", function() {
require("./GIFTS/CREATE.js")().then(function(data) {
createdGift = data;
});
});
describe("READ FROM WEB", function() {
require("./GIFTS/READ FROM WEB.js")(createdGift).then(function(data) {
});
});
});
Content of "./GIFTS/CREATE.js"
module.exports = function() {
return new Promise(function(resolve, reject) {
//DO SOME TESTS WITH IT AND DESCRIBE
after(function() {
resolve(createdGift);
});
});
};
The problem is that the test are immediately initialized by mocha and so in the second test suite "READ FROM WEB" the value passed as createdGift is given immediately to the test without waiting for the first test to plete and so undefined is passed.
Jankapunkt's Answer
This is how I tried in my code:
var create = require("./GIFTS/CREATE");
var read = require("./GIFTS/READ FROM WEB");
describe.only("Gifts Workflow", function() {
create(function(createdGift) {
read(createdGift);
});
});
CREATE
module.exports = function(callback) {
var createdGift;
//SOME TESTS
describe("it should insert a gift", function() {
var result;
before(function() {
return request
.post(url)
.then(function(res) {
createdGift = res.body;
});
});
//SOME OTHER TESTS
});
after(function() {
callback(createdGift);
});
};
READ FROM WEB
module.exports = function(createdGift) {
it("should be restricted", function(done) {
request
.get(url)
.query({})
.end(function(err, res) {
expect(res.statusCode).to.equal(400);
done();
});
});
describe("it should read all gifts", function() {
//SOME TESTS
});
};
And this is the output
Gifts Workflow
✓ should be restricted
✓ should not work when inplete payload is specified
it should insert a gift
✓ should return 200
✓ should return an object
✓ should have uploaded the image to S3 (598ms)
✓ should close db connections
it should read all gifts
✓ should return 200
✓ should return an array
✓ should contain the previously added gift
✓ should close db connections
10 passing (3s)
It may seems it's working but as you can see from the tabulation it should read all gifts is not a children of Gifts Workflow but is a child of the root suite.
This is what happens:
- Mocha calls the root suite
- Mocha finds the Gifts Workflow suite and executes the create() function inside this suite
- Since the function is asynchronous Mocha thinks that the Gifts Workflow suite is ended and returns to the root suite
- read() is executed
- Mocha exit from the root suite and goes to the next tests because ,being asynchronous, it thinks that all the test are finished
- Test #3,4,5,... are never called
Can you confirm this is your case alse with more that two tests ?
I'm building some API tests for the product I'm building. One of the test looks like this:
GET FILTERS
✓ should be restricted (45ms)
it should get the filters
✓ should return 200
✓ should return an object
✓ should close db connections
GET USERS COUNT
✓ should be restricted
✓ should throw error when payload is not correct
it should get the user count
✓ should return 200
✓ should return an object
✓ should close db connections
GET USERS FILE
✓ should be restricted
✓ should throw error when no queryId is specified
it should retrieve the file
✓ should return 200
✓ should download an excel file
✓ should close db connections
UPLOAD PROMOTION IMAGE
✓ should throw error when no file is specified
it should save the file
✓ should return 200
✓ should have named the file with the title of the promotion
✓ should have uploaded the file to S3 (355ms)
✓ should close db connections
CREATE PROMOTION
it should save the promotion
✓ should return 200
✓ should return a correct response
✓ should close db connections
GET PROMOTIONS
✓ should be restricted
it should get the promotions
✓ should return 200
✓ should be an array of promotions
✓ should contain the previously created promotion
UPDATE PROMOTION
it should update the promotion
✓ should return 200
✓ should return a correct response
✓ should close db connections
PUT PROMOTION IN BIN
it should put the promotion in the bin
✓ should return 200
✓ should return a correct response
✓ should close db connections
GET ARCHIVED PROMOTIONS
✓ should be restricted
it should get the promotions
✓ should return 200
✓ should be an array of promotions
✓ should be an array of archived promotions
✓ should contain the previously archived promotion
DELETE PROMOTION
it should delete the promotion
✓ should return 200
✓ should return a correct response
✓ should have deleted the file from S3 (563ms)
✓ should close db connections
As you can see I've tried to put everything regarding promotions in a single test suite so that I can have a sort of workflow to test what the user will do exactly on the platform.
In this example I create a random generate promotion and then use the id of that promotion to read it, update it, archive it and then finally delete it. Every step is connected and I need to have return values for each suit (i.e: The ID of the inserted promotion, or the filters...)
At this moment my promotions.test.js file is 625 and, since I have not finished yet, I expect it to grow a lot in the next days.
Is there a way to split multiple test suits in different files but in a way that each test/file is able to return, as soon as it finishes, a value that I can pass to the next step ?
EDIT FOR BOUNTY
At the moment I only tried something like this:
describe.only("Gifts Workflow", function() {
var createdGift;
describe("CREATE", function() {
require("./GIFTS/CREATE.js")().then(function(data) {
createdGift = data;
});
});
describe("READ FROM WEB", function() {
require("./GIFTS/READ FROM WEB.js")(createdGift).then(function(data) {
});
});
});
Content of "./GIFTS/CREATE.js"
module.exports = function() {
return new Promise(function(resolve, reject) {
//DO SOME TESTS WITH IT AND DESCRIBE
after(function() {
resolve(createdGift);
});
});
};
The problem is that the test are immediately initialized by mocha and so in the second test suite "READ FROM WEB" the value passed as createdGift is given immediately to the test without waiting for the first test to plete and so undefined is passed.
Jankapunkt's Answer
This is how I tried in my code:
var create = require("./GIFTS/CREATE");
var read = require("./GIFTS/READ FROM WEB");
describe.only("Gifts Workflow", function() {
create(function(createdGift) {
read(createdGift);
});
});
CREATE
module.exports = function(callback) {
var createdGift;
//SOME TESTS
describe("it should insert a gift", function() {
var result;
before(function() {
return request
.post(url)
.then(function(res) {
createdGift = res.body;
});
});
//SOME OTHER TESTS
});
after(function() {
callback(createdGift);
});
};
READ FROM WEB
module.exports = function(createdGift) {
it("should be restricted", function(done) {
request
.get(url)
.query({})
.end(function(err, res) {
expect(res.statusCode).to.equal(400);
done();
});
});
describe("it should read all gifts", function() {
//SOME TESTS
});
};
And this is the output
Gifts Workflow
✓ should be restricted
✓ should not work when inplete payload is specified
it should insert a gift
✓ should return 200
✓ should return an object
✓ should have uploaded the image to S3 (598ms)
✓ should close db connections
it should read all gifts
✓ should return 200
✓ should return an array
✓ should contain the previously added gift
✓ should close db connections
10 passing (3s)
It may seems it's working but as you can see from the tabulation it should read all gifts is not a children of Gifts Workflow but is a child of the root suite.
This is what happens:
- Mocha calls the root suite
- Mocha finds the Gifts Workflow suite and executes the create() function inside this suite
- Since the function is asynchronous Mocha thinks that the Gifts Workflow suite is ended and returns to the root suite
- read() is executed
- Mocha exit from the root suite and goes to the next tests because ,being asynchronous, it thinks that all the test are finished
- Test #3,4,5,... are never called
Can you confirm this is your case alse with more that two tests ?
Share Improve this question edited May 15, 2017 at 15:53 Matteo Cardellini asked May 7, 2017 at 21:38 Matteo CardelliniMatteo Cardellini 8962 gold badges17 silver badges42 bronze badges 5- not sure if this helps, but it seems pretty relevant to me: stackoverflow./questions/28229424/… – LostJon Commented May 8, 2017 at 18:36
- Yes but I need a way to return a value after every test suite... – Matteo Cardellini Commented May 8, 2017 at 18:57
-
Gotcha, so you are chaining. You need to declare your variable outside of the
it()
scope. I normally do this right below mydescribe()
statement. Now, if you need this variable to be passed from file to file, not sure if the above will work...IT MIGHT, but not sure. So, you would essentially have a controller test-spec that would import your other specs. – LostJon Commented May 8, 2017 at 19:02 - The problem is also that describe is syncronous and the api calls are asyncronous... so if i call a file with require() for example and then return a value with module.exports it will return an undefined since it hasn't run the test yet.... I could return a promise but mocha needs to access all the test and THEN execute them. If the test are in a promise mocha will not see them – Matteo Cardellini Commented May 8, 2017 at 19:08
- 1 yea, that gets a bit gross. sounds like you would need to module.export a promise, then handle the resolve of such (in which you will require your next spec file). just talking outloud here... – LostJon Commented May 8, 2017 at 19:15
2 Answers
Reset to default 1I was dealing with a similar issue and I found at least workaraound. Please ment, if something is not working.
I came up with this, when I found out, that mocha only auto-executes the describe blocks, when they are in module-scope but not inside a function.
It breaks down to the following approach:
- Wrap all describe blocks inside functions, which are exported by your modules
- Pass callbacks to the functions which provide the return value
- Call functions in sequence using callbacks in your test suite
Reproducable Example
Create minimal test setup
index.js
test1.js
test2.js
In your test files you wrap up your tests in exported functions. Note, that I use ES6 for import/export the modules.
test1.js
export const test1_method = function(callback){
let returnValue; // declared outside the tests
describe("test 1", function(){
it ("1. unit", function(){
assert.isTrue(true);
// assigned inside test
returnValue = 42;
});
it ("2. unit", function(){
assert.isTrue(true);
callback(returnValue); // called in the last unit
});
});
}
As you can see, this function has a simple callback passed and which is called in the very last unit. You might argue, that this is very vague. I agree, but in practice I have never seen a sequence randomness of it-units inside a describe block. Thus you can assume, that the callback will be called after your last unit has run.
test2.js
export const test2_method = function(previousValue){
describe("test 2", function(){
it ("runs correctly with a dependency value", function(){
assert.equal(previousValue, 42);
})
})
}
Not much to add here, just takes the input and tests for a specific value.
index.js
import {test1_method} from './test1.js';
import {test2_method} from './test2.js';
test1_method(function(test1Result){
// run the other tests in the callback
test2_method(test1Result);
});
Here is, where you glue the tests together. This would be the root of your suite. You call the first method and provide a callback, where you end up with your result to be passed to your test2 method. The result of your first test is surprisingly not undefined and you can easily pass it as parameter to test2.
Output
I20170515-15:09:33.768(2)? test 1
I20170515-15:09:33.769(2)?
I20170515-15:09:33.770(2)? ✓ 1. unit
I20170515-15:09:33.770(2)?
I20170515-15:09:33.771(2)? ✓ 2. unit
I20170515-15:09:33.771(2)?
I20170515-15:09:33.772(2)? test 2
I20170515-15:09:33.773(2)?
I20170515-15:09:33.773(2)? ✓ runs correctly with a dependency value
Advantages
You can control your test-order and split your suite into sub-suites.
You write your tests parameterized, which als makes them reusable, depending on the use case. In my test suite there is for example one function with a describe set of 10 tests, which is applied for all my mongo collections.
Disadvantages
You need to rewrite all your tests into wrapped function, so that no test is auto-executed by mocha but only by your test suite.
Multiple callbacks inside callbacks makes it hard to read and debug.
Summary
In my opinion not an "official" solution but a workaround and starting point from where you can improve your test suit, if no other solution is found.
You're missing before
in your describe blocks. The below works for me.
/test/helpers/gifts/create.js
module.exports = () => {
const aGift = 'a gift';
console.log('creating: ' + aGift);
return Promise.resolve(aGift);
};
/test/helpers/gifts/read-from-web.js
module.exports = createdGift => {
console.log('read-from-web got: ' + createdGift);
return Promise.resolve('something read from the web');
};
/test/index.js
//---------//
// Imports //
//---------//
const chai = require('chai')
, create = require('./helpers/gifts/create')
, readFromWeb = require('./helpers/gifts/read-from-web')
;
//------//
// Init //
//------//
chai.should();
//------//
// Main //
//------//
describe('gifts workflow', () => {
var createdGift
, somethingReadFromTheWeb
;
describe('create', () => {
before(() => create()
.then(data => {
createdGift = data;
})
);
it('created gift should exist', () => {
createdGift.should.equal('a gift');
});
});
describe('read from web', () => {
before(() => readFromWeb(createdGift)
.then(data => {
somethingReadFromTheWeb = data;
})
);
it('something should be read from the web', () => {
somethingReadFromTheWeb.should.equal('something read from the web');
});
});
});
result output (forgive the mess)
$ mocha
gifts workflow
create
creating: a gift
✓ created gift should exist
read from web
read-from-web got: a gift
✓ something should be read from the web
2 passing (10ms)
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744957382a4603275.html
评论列表(0条)