最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Resolving promises in Protractor and Cucumber using Chai as Promised - Stack Overflow

programmeradmin5浏览0评论

Lately a colleague and I have had some disagreements on the "right" way to implement Cucumber step definitions using Protractor and Chai as Promised. Our contention es from a mutual lack of understanding of precisely what is going with promise resolution in the context of Cucumber.

We're testing against an AngularJS application, so resolving promises and asynchronous behavior is a necessary evil. The biggest problem we've had is forcing synchronous test behavior and getting Cucumber to wait on promises between step definitions. In some cases, we've observed situations such that Cucumber seems to plow straight through step definitions before Webdriver even executes them. Our solutions to this problem vary...

Consider the hypothetical scenario:

Scenario: When a user logs in, they should see search form
  Given a user exists in the system
  When the user logs into the application
  Then the search form should be displayed

Most of the confusion originates in the Then step. In this example the definition should assert that all fields for the search form exist on the page, meaning multiple isPresent() checks.

From the documentation and examples I was able to find, I felt the assertion should look something like this:

this.Then(/the search form should be displayed/, function(next) {
    expect(element(by.model('searchTerms')).isPresent()).to.eventually.be.true;
    expect(element(by.model('optionsBox')).isPresent()).to.eventually.be.true;
    expect(element(by.button('Run Search')).isPresent()).to.eventually.be.true.and.notify(next);
});

However, my colleague contends that in order to satisfy promise resolution, you need to chain your expects with then() like this:

this.Then(/the search form should be displayed/, function(next) {
    element(by.model('searchTerms')).isPresent().then(function(result) {
        expect(result).to.be.true;

    }).then(function() {
        element(by.model('optionsBox')).isPresent().then(function(result) {
            expect(result).to.be.true;

        }).then(function() {
            element(by.button('Run Search')).isPresent().then(function(result) {
                expect(result).to.be.true;
                next;
            });
        });
    });
});

The latter feels really wrong to me, but I don't really know if the former is correct either. The way I understand eventually() is that it works similarly to then(), in that it waits for the promise to resolve before moving on. I would expect the former example to wait on each expect() call in order, and then call next() through notify() in the final expect() to signal to cucumber to move on to the next step.

To add even more confusion, I've observed other colleagues write their expects like this:

expect(some_element).to.eventually.be.true.then(function() {
    expect(some_other_element).to.eventually.be.true.then(function() {
        expect(third_element).to.eventually.be.true.then(function() {
            next();
        });
    });
});

So the questions I think I'm alluding to are:

  • Is any of the above even kinda right?
  • What does eventually() really do? Does it force synchronous behavior like then()?
  • What does and.notify(next) really do? Is it different from calling next() inside of a then()?
  • Is there a best practices guide out there that we haven't found yet that gives more clarity on any of this?

Many thanks in advance.

Lately a colleague and I have had some disagreements on the "right" way to implement Cucumber step definitions using Protractor and Chai as Promised. Our contention es from a mutual lack of understanding of precisely what is going with promise resolution in the context of Cucumber.

We're testing against an AngularJS application, so resolving promises and asynchronous behavior is a necessary evil. The biggest problem we've had is forcing synchronous test behavior and getting Cucumber to wait on promises between step definitions. In some cases, we've observed situations such that Cucumber seems to plow straight through step definitions before Webdriver even executes them. Our solutions to this problem vary...

Consider the hypothetical scenario:

Scenario: When a user logs in, they should see search form
  Given a user exists in the system
  When the user logs into the application
  Then the search form should be displayed

Most of the confusion originates in the Then step. In this example the definition should assert that all fields for the search form exist on the page, meaning multiple isPresent() checks.

From the documentation and examples I was able to find, I felt the assertion should look something like this:

this.Then(/the search form should be displayed/, function(next) {
    expect(element(by.model('searchTerms')).isPresent()).to.eventually.be.true;
    expect(element(by.model('optionsBox')).isPresent()).to.eventually.be.true;
    expect(element(by.button('Run Search')).isPresent()).to.eventually.be.true.and.notify(next);
});

However, my colleague contends that in order to satisfy promise resolution, you need to chain your expects with then() like this:

this.Then(/the search form should be displayed/, function(next) {
    element(by.model('searchTerms')).isPresent().then(function(result) {
        expect(result).to.be.true;

    }).then(function() {
        element(by.model('optionsBox')).isPresent().then(function(result) {
            expect(result).to.be.true;

        }).then(function() {
            element(by.button('Run Search')).isPresent().then(function(result) {
                expect(result).to.be.true;
                next;
            });
        });
    });
});

The latter feels really wrong to me, but I don't really know if the former is correct either. The way I understand eventually() is that it works similarly to then(), in that it waits for the promise to resolve before moving on. I would expect the former example to wait on each expect() call in order, and then call next() through notify() in the final expect() to signal to cucumber to move on to the next step.

To add even more confusion, I've observed other colleagues write their expects like this:

expect(some_element).to.eventually.be.true.then(function() {
    expect(some_other_element).to.eventually.be.true.then(function() {
        expect(third_element).to.eventually.be.true.then(function() {
            next();
        });
    });
});

So the questions I think I'm alluding to are:

  • Is any of the above even kinda right?
  • What does eventually() really do? Does it force synchronous behavior like then()?
  • What does and.notify(next) really do? Is it different from calling next() inside of a then()?
  • Is there a best practices guide out there that we haven't found yet that gives more clarity on any of this?

Many thanks in advance.

Share Improve this question asked Jul 13, 2015 at 18:29 Andrew YochumAndrew Yochum 951 silver badge6 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 4
  • Your feeling was correct, your colleague was wrong (though it was a reasonable mistake!). Protractor automatically waits for one WebDriver mand to resolve before running a second. So in your second code block, element(by.button('Run Search')).isPresent() will not resolve until both element(by.model('optionsBox')).isPresent() and element(by.model('searchTerms')).isPresent() are done.
  • eventually resolves promises. An explanation is here: https://stackoverflow./a/30790425/1432449
  • I do not believe it's different from putting next() inside of then()
  • I do not believe there is a best practices guide. Cucumber is not a core focus of the Protractor team, and support for it is largely provided by the munity on github. If you or someone you know would like to write a best practices guide, we (the protractor team) would wele a PR!

What works for me is this - The function below searches for something that will always equal true - in the case the existence of a html tag. I call this function at the end of each test, passing in the callback

function callbackWhenDone(callback) {
    browser.wait(EC.presenceOf(element(by.css('html'))))
        .then(function () {callback();})
}

This is the usage in a simple test:

this.Given(/^I go on "([^"]*)"$/, function (arg1, callback) {
    browser.get('/' + arg1);
    callbackWhenDone(callback);
});

A bit of a hack I know but it gets the job done and looks pretty clean when used everywhere

发布评论

评论列表(0)

  1. 暂无评论