I'm working with selenium to write a test suite. The project is in node. I want to refactor the code into a BDD/cucumber model. It looks like using cucumber-js, asynchronous code inside step definitions is not being executed when I run a feature.
Currently I'm using Mocha to manage all the Promises returned from the selenium library and ensure all the asynch operations are run. There is an alternate Cucumber library (gherkin-mocha) that is much older and less maintained that might work.
How can I run asynch operations inside Cucumber steps? Is it possible to use Mocha alongside this?
I figured out the problem.
Step definition methods take a regex and a function. The function parameter can take a callback which it needs to call at the end of its execution. My previous code looked like this
this.Given(/I click something/, function(done) {
// do stuff
done();
}
To use promise and asynch behavior, omit the "done" parameter:
this.Given(/I click something/, function() {
// do stuff
return Promise.resolve();
}
Related
There are several 'After' hooks and one of them should be first than others, how it could be configured in the Cucumber JS?
You can explicitly configure hooks to run in a certain order:
#Before(order = 10) // Annotated method
public void doSomething(){
// Do something before each scenario
}
Before(10, () -> { // Lambda
// Do something before each scenario
});
It seems this also works for #After hooks.
Edit: Leaving this in case it's useful for any Java people - missed that it was JS, sorry! But for Javascript:
Hooks are executed in the order in which they're defined. If that doesn't do it for you, create one hook and explicitly call the other methods.
Ordered by top to bottom in the hooks file
After({}, async function() {
return "This will run first";
});
After({}, async function() {
return "tThis will run second";
});
You can use tags concept for each scenario in cucumber feature file and use same tag on each #After annotation which will resolve for which test scenario's that specific #After should execute
Hooks is a file which will be executed from top to bottom. If you have more After tags, go in such a way that which you want to close first of all will be first and the last what you want to execute to be last. If you have tags in your feature file, pass that information to the specific After
I trying to convert a nodejs project to TypeScript and while mostly I did not faced really difficult obstacles during this process, the codebase has few gotchas like this, mostly in startup code:
function prepareConfiguration() {
let cloudConfigLoader = require('../utils/cloud-config');
return cloudConfigLoader.ensureForFreshConfig().then(function() {
//do some stuff
});
}
I may be need just an advice on which approach for refactoring this has less code changes to be made to make it work in TypeScript fashion.
In response to comments, more details:
That require loads the node module, not a JSON file. From that module the ensureForFreshConfig function contacts with a cloud service to load a list of values to rebuild a configuration state object.
Problem is that mdule was made in standard node approach of "module is isngleton object" and its independencies include auth component that will be ready only when the shown require call is made. I know it is not best a way to do so..
Typesript does not allow "mport that module later" except with dynamyc import which is problematic as mentiond in comment.
The "good" approach is to refactor that part of startup and make the ensureForFreshConfig and its dependency to initiate its ntenras on demand via cnstructors.. I just hoped ofr some soluiton to reduce things to be remade durng this transition to the TypeScript
import { cloudConfigLoader } from '../utils/cloud-config'
async function prepareConfiguration() {
await cloudConfigLoader.ensureForFreshConfig()
// do some stuff
// return some-stuff
}
The function is to be used as follows.
await prepareConfiguration()
Here is a sample code to make it more clear.
This is my module:
module.exports = ((parameter) => {
console.log(parameter);
})('some value');
mymodule is actually the main server file and thus has to be executed immediately.
My unit tests in test suite require my module but the immediately invoked function expression is executed only first time.
require('./mymodule');
require('./mymodule');
How can I make this run every time ?
You can invalidate the cache that Node maintains:
require('./mymodule');
delete require.cache[require.resolve('./mymodule')]
require('./mymodule');
You are probably better off though with exporting the function and calling it when needed, instead of only exporting the result.
From nodejs docs:
Multiple calls to require('foo') may not cause the module code to be
executed multiple times. This is an important feature. With it,
"partially done" objects can be returned, thus allowing transitive
dependencies to be loaded even when they would cause cycles.
https://nodejs.org/docs/v0.4.12/api/modules.html#caching
If you want to have a module execute code multiple times, then export a function, and call that function.
I am trying to create a Node module (using harmony) that upon loading by another module/application, has to be yielded to so that things in it's construct can be executed and loaded before any of it's exposed functions can be called.
The issue I am having is that I cannot seem to yield to the internal function that is being executed, using module.exports. An example would help.
module.exports = function*(s_id){
console.log('loading the module lets it execute up till here');
if (!(this instanceof Tester)) return yield new Tester();
}
function* Tester(){
console.log('but we never execute this generator function');
}
Tester.prototype = {
model : function*(){
// other functions
}
}
It's been stumping me for hours now! I feel like the solution is super simple but I cannot seem to wrap my head around it. I have tried to simply make the Tester() function the export, but am still having the same issue. Why can't I seem to yield to the Tester() function?
Also, what may an alternative be to this approach? I want to maintain the Object nature of the module so that the module can be loaded with different inputs, such as the s_id variable/object in the example above.
a Node module (using harmony) that upon loading by another module/application, has to be yielded to so that things in it's construct can be executed and loaded before any of it's exposed functions can be called
Don't do that. Generators are not made for asynchrony. yield doesn't do what you want here. A module is not "yielded" to await something in it to load. yield is magic, but not async magic.
If you must use an asynchronous module loading process, export a promise for your actual module. That is a standard interface for something to await, and can be consumed using a standardized approach that does not rely on internals of your module.
You still can use yield syntax for constructing that promise, just use your favorite coroutine library.
return yield new Tester();
…
function* Tester(){…}
Uh oh. Well yes, apparently it is possible to call generator functions as constructors. But believe me, it is not what you want. A constructor for an arbitrary object should return that object, instead of an iterator (just like it shouldn't return a promise). If some of your object methods are generator methods, that's fine, but your constructor should be not.
If you really want to use a generator function in your code like that (and it's not meant to be a constructor), you
will need to yield* the iterator you've created (tester()) instead of yielding it
must not overwrite its .prototype property like you did, as that causes the iterator to malfunction. (Of course you should never do that at all, even though most of the time it works)
Since we've switched to generators I haven't been able to find a coverage tool to support this.
We use generators both in our code and in the mocha tests themselves.
We have enabled generators within the mocha tests by using co-mocha.
The only option I have in mind would be to transpile the tests instead of running them in their harmony mode.
Unit-coverage have basic harmony support.
Details here.
But, I can't use this tool correctly: I have error, when use class.
Awhile back co-mocha worked nicely for me. Later on in a new codebase it didn't. After some digging I found that mocha simply isn't handling promises properly anymore and it's probably related to node 5; I've logged a ticket.
With promise handling working properly, you theoretically shouldn't need to monkeypatch mocha to use generators. Simply using co.wrap() like so should work:
it('should yield in the generator', co.wrap(function*() {
yield aPromiseReturningFunction();
yield aGeneratorFunction();
}));
In the meantime, I've written a lil utility function that wraps a generator using co.wrap() and passes done as the then and catch handlers to the promise it returns:
function done(gen) {
const wrapper = co.wrap(gen);
return function(done) {
return wrapper.call(this, done).then(done).catch(done);
};
}
Then I do this:
it('should yield in the generator', done(function*() {
yield aPromiseReturningFunction();
yield aGeneratorFunction();
}));