I have created tests for my application. Everything works but it runs slow and even though only 1/3 of the application is tested it still takes around ten minutes for protrator to create the test data, fill out the fields, click the submit button etc.
I am using Google Crome for the testing. It seems slow as I watch protractor fill out the fields one by one.
Here's an example of my test suite:
suites: {
login: ['Login/test.js'],
homePage: ['Home/test.js'],
adminPage: ['Admin/Home/test.js'],
adminObjective: ['Admin/Objective/test.js'],
adminObjDetail: ['Admin/ObjectiveDetail/test.js'],
adminTopic: ['Admin/Topic/test.js'],
adminTest: ['Admin/Test/test.js'],
adminUser: ['Admin/User/test.js'],
adminRole: ['Admin/Role/test.js']
},
This is one test group:
login: ['Login/test.js'],
homePage: ['Home/test.js'],
adminUser: ['Admin/User/test.js'],
adminRole: ['Admin/Role/test.js']
This is another test group:
adminPage: ['Admin/Home/test.js'],
adminObjective: ['Admin/Objective/test.js'],
adminObjDetail: ['Admin/ObjectiveDetail/test.js'],
adminTopic: ['Admin/Topic/test.js'],
adminTest: ['Admin/Test/test.js'],
The two groups can run independently but they must run in the order I have above. After the answers I did read about sharing but I am not sure if this helps my situation as my tests need to be run in order. Ideally I would like to have one set of tests run in one browser and the other set in another browser.
I read about headless browsers such as PhantomJS. Does anyone have experience with these being faster?
Any advice on how I could do this would be much appreciated.
We currently use "shardTestFiles: true" which runs our tests in parallel, this could help if you have multiple tests.
I'm not sure what you are testing here, whether its the data creation or the end result. If the latter, you may want to consider mocking the data creation instead or bypassing the UI some other way.
Injecting in Data
One thing that you can do that will give you a major boost in performance is to not double test. What I mean by this is that you end up filling in dummy data a number of times to get to a step. Its also one of the major reasons that people need tests to run in a certain order (to speed up data entry).
An example of this is if you want to test filtering on a grid (data-table). Filling in data is not part of this action. Its just an annoying thing that you have to do to get to testing the filtering. By calling a service to add the data you can bypass the UI and seleniums general slowness (Id also recommend this on the server side by injecting values directly into the DB using migrations).
A nice way to do this is to add a helper to your pageobject as follows:
module.exports = {
projects: {
create: function(data) {
return browser.executeAsyncScript(function(data, callback) {
var api = angular.injector(['ProtractorProjectsApp']).get('apiService');
api.project.save(data, function(newItem) {
callback(newItem._id);
})
}, data);
}
}
};
The code in this isnt the cleanest but you get the general gist of it. Another alternative is to replace the module with a double or mock using [Protractor#addMockModule][1]. You need to add this code before you call Protractor#get(). It will load after your application services overriding if it has the same name as an existing service.
You can use it as follows :
var dataUtilMockModule = function () {
// Create a new module which depends on your data creation utilities
var utilModule = angular.module('dataUtil', ['platform']);
// Create a new service in the module that creates a new entity
utilModule.service('EntityCreation', ['EntityDataService', '$q', function (EntityDataService, $q) {
/**
* Returns a promise which is resolved/rejected according to entity creation success
* #returns {*}
*/
this.createEntity = function (details,type) {
// This is your business logic for creating entities
var entity = EntityDataService.Entity(details).ofType(type);
var promise = entity.save();
return promise;
};
}]);
};
browser.addMockModule('dataUtil', dataUtilMockModule);
Either of these methods should give you a significant speedup in your testing.
Sharding Tests
Sharding the tests means splitting up the suites and running them in parallel. To do this is quite simple in protractor. Adding the shardTestFiles and maxInstences to your capabilities config should allow you to (in this case) run at most two test in parrallel. Increase the maxInstences to increase the number of tests run. Note : be careful not to set the number too high. Browsers may require multiple threads and there is also an initialisation cost in opening new windows.
capabilities: {
browserName: 'chrome',
shardTestFiles: true,
maxInstances: 2
},
Setting up PhantomJS (from protractor docs)
Note: We recommend against using PhantomJS for tests with Protractor. There are many reported issues with PhantomJS crashing and behaving differently from real browsers.
In order to test locally with PhantomJS, you'll need to either have it installed globally, or relative to your project. For global install see the PhantomJS download page (http://phantomjs.org/download.html). For local install run: npm install phantomjs.
Add phantomjs to the driver capabilities, and include a path to the binary if using local installation:
capabilities: {
'browserName': 'phantomjs',
/*
* Can be used to specify the phantomjs binary path.
* This can generally be ommitted if you installed phantomjs globally.
*/
'phantomjs.binary.path': require('phantomjs').path,
/*
* Command line args to pass to ghostdriver, phantomjs's browser driver.
* See https://github.com/detro/ghostdriver#faq
*/
'phantomjs.ghostdriver.cli.args': ['--loglevel=DEBUG']
}
Another speed tip I've found is that for every test I was logging in and logging out after the test is done. Now I check if I'm already logged in with the following in my helper method;
# Login to the system and make sure we are logged in.
login: ->
browser.get("/login")
element(By.id("username")).isPresent().then((logged_in) ->
if logged_in == false
element(By.id("staff_username")).sendKeys("admin")
element(By.id("staff_password")).sendKeys("password")
element(By.id("login")).click()
)
I'm using grunt-protractor-runner v0.2.4 which uses protractor ">=0.14.0-0 <1.0.0".
This version is 2 or 3 times faster than the latest one (grunt-protractor-runner#1.1.4 depending on protractor#^1.0.0)
So I suggest you to give a try and test a previous version of protractor
Hope this helps
Along with the great tips found above I would recommend disabling Angular/CSS Animations to help speed everything up when they run in non-headless browsers. I personally use the following code in my Test Suite in the "onPrepare" function in my 'conf.js' file:
onPrepare: function() {
var disableNgAnimate = function() {
angular
.module('disableNgAnimate', [])
.run(['$animate', function($animate) {
$animate.enabled(false);
}]);
};
var disableCssAnimate = function() {
angular
.module('disableCssAnimate', [])
.run(function() {
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '* {' +
'-webkit-transition: none !important;' +
'-moz-transition: none !important' +
'-o-transition: none !important' +
'-ms-transition: none !important' +
'transition: none !important' +
'}';
document.getElementsByTagName('head')[0].appendChild(style);
});
};
browser.addMockModule('disableNgAnimate', disableNgAnimate);
browser.addMockModule('disableCssAnimate', disableCssAnimate);
}
Please note: I did not write the above code, I found it online while looking for ways to speed up my own tests.
From what I know:
run test in parallel
inject data in case you are only testing a UI element
use CSS selectors, no xpath (browsers have a native engine for CSS, and the xpath engine is not performance as CSS engine)
run them on high performant machines
use as much as possible beforeAll() and beforeEach() methods for instructions that you repeat often in multiple test
Using Phantomjs will considerably reduce the duration it takes in GUI based browser, but better solution I found is to manage tests in such a way that it can be run in any order independently of other tests, It can be achieved easily by use of ORM(jugglingdb, sequelize and many more) and TDB frameworks, and to make them more manageable one can use jasmine or cucumber framework, which has before and after hookups for individual tests. So now we can gear up with maximum instances our machine can bear with "shardTestFiles: true".
Related
I'm using Webdriver.io to download a file continuously
I tried the following code:
var webdriverio = require('webdriverio');
var options = {
desiredCapabilities: {
browserName: 'chrome'
// waitforTimeout: 1000000
}
};
webdriverio
.remote(options)
.init()
.url('https://xxx')
.setValue('#username', ‘xxx#gmail.com’)
.click('#login-submit')
.pause(1000)
.setValue('#password’,’12345’)
.click('#login-submit')
.getTitle().then(function(title){
console.log('Title was: ' + title);
})
.pause(20000)
.getUrl().then(function(url){
console.log('URL: ' + url);
})
.getTitle().then(function(title){
console.log('Title was: ' + title);
})
.click("a[href='/wiki/admin'] button.iwdh")
.getUrl().then(function (url) {
console.log('URL after settings ' + url);
})
.pause(3000)
.scroll('div.jsAtfH',0,1000)
.click("a[href='/wiki/plugins/servlet/ondemandbackup/admin']")
.pause(10000)
.click('//*[#id="backup"]/a')
//.pause(400000)
.end();
Note: The file size is 7GB and how long it will take to download is depend upon the network so instead of using pause() and timeout() is there any way to do it using webdriver.io or node.js ?
To begin with, your current task (waiting for a HUUUUGE file to download) is not a common use-case when it comes to Webdriver-based automation frameworks, WebdriverIO included. Such frameworks aren't meant to download massive files.
First off, you're confusing the waitforTimeout value with WebdriverIO test timeout. Your test is timing out before the .pause() ends.
Currently you're running your tests via the WebdriverIO test-runner. If you want to increase the test timeout, you have to use a different test framework (Mocha, Jasmine, or Cucumber) and set its timeout value to w/e you find appropriate. Going on, I recommend you use Mocha (coming from an ex-Cucumber guy).
You will have to install Mocha: npm install --save-dev wdio-mocha-framework and run your tests with it. Your test should look like this afterwards:
describe("Your Testsuite", function() {
it("\nYour Testcase\n", function() {
return browser
.url('https://xxx')
.setValue('#username', ‘xxx#gmail.com’)
.click('#login-submit')
// rest of the steps
.scroll('div.jsAtfH',0,1000)
.click("a[href='/wiki/plugins/servlet/ondemandbackup/admin']")
.pause(10000)
.click('//*[#id="backup"]/a')
)}
)}
Your config (wdio.conf.js) should contain the following:
framework: 'mocha',
mochaOpts: {
ui: 'bdd',
timeout: 99999999
}
As a side-note, I tried waiting a very long time (> 30 mins) using the above config and had no issues what-so-ever.
Let me know if this helps. Cheers!
If you click on a download button in your browser and you close your browser then your download will be also closed. If you are owning the website where you click on the download button then try to rewrite your code that you have a download able url. Then you can search for a module or way to download files from http url. If you are not the owner and you cant find a url in the href then you can maybe get the generated download url from the network section at your inspector.
Also I never heard that a browser gets closed after timeout? Maybe it comes from webdriver.io I never let my chrome so long open with webdriver.io
You can try to make a workaround use Intervall each 1 Minute as example and then use a webdriver.io command to don´t timeout.
I know it's very old question but I wanted to answer question from comment (and have no such possibility yet). But I will answer main question too.
When i am giving timeout in "wdio.conf.js" file it's not able to
downlaod file it's closing the session but by giving .pause(2000000)
in webdriver.io code it's able to download file of 7GB. What is the
use of timeout in "wdio.conf.js" file if it's kicking out the session
without downlaod?
So this timeout is related to elements state during the test run. So it "determines how long the instance should wait for that element to reach the state".
https://webdriver.io/docs/timeouts.html - this can help. But to answer the question too:
There are more many timeouts such test deals with. Like iamdanchiv wrote for this you should try using one of automatically supported frameworks like Mocha or Jasmine.
IMO right now the easiest way would to do the quick fresh setup using CLI provided by WDIO:
https://webdriver.io/docs/gettingstarted.html
Where you can just simply pick the additional framework you want to use. I would suggest using Jasmine and Chromedriver for this. Than in your wdio.conf.js you can change this part:
waitforTimeout: 10000,
jasmineNodeOpts: {
// Jasmine default timeout
defaultTimeoutInterval: 60000,
//
},
To something that works for you. Or you can use boilerplate projects from wdio page like this one:
https://webdriver.io/docs/boilerplate.html
But that's not all! Still you will have to create some method or function that checks for the file. So check where do you download the file or make it download where you want to and then create a method that uses some kind of wait:
https://webdriver.io/docs/api/browser/waitUntil.html
browser.waitUntil(condition, { timeout, timeoutMsg, interval })
So you can set the timeout either here or in wdio.conf in 'waitforTimeout'. Inside this method condition you can use node filesystem (https://nodejs.org/api/fs.html) to check the state of the file.
This can be helpful to get through waiting for file condition:
https://blog.kevinlamping.com/downloading-files-using-webdriverio/
I'm just getting into testing my node app with Protractor. Running into an issue with various resolutions, sometimes buttons are hidden/show depending on the screen size & making tests fail occasionally that shouldn't.
Is there a way to set up my tests so that certain tests run with certain resolutions & others are excluded?
Can someone point me to some resources they've found?
If you can group the the different types of specs by file then you should use multicapabilities. See https://stackoverflow.com/a/27489765/689411
If you need to mix tests in the same file that each have a special required resolution you can change the resolution once per test
var switchToMobileResolution = function() { browser.manage().window().setSize(500, 900); }
it('does something that only works on mobile',function(){
switchToMobileResolution();
//do something...
});
Another thing you could do is you could write a simple jasmine reporter that switches resolutions if it matches a special string in the spec name...
var mobileResolutionSpecKey = 'on mobile';
var myReporter = {
specStarted: function(result) {
if(result.fullName.match(mobileResoultionSpecKey) !== null){
switchToMobileResolution();
}
}
jasmine.getEnv().addReporter(myReporter);
Is there way to merge 2 commands into 1 command
protractor protractor.conf --suite create_buyer --params.buyerName=Buyer1
protractor protractor.conf --suite create_buyer --params.buyerName=Buyer2
like
protractor protractor.conf --suite create_buyer,create_buyer --params.suites[0].buyerName=Buyer1 --params.suites[1].buyerName=Buyer2
to make this commad work i need to know the current suite index
is it possible?
may there is better way!
As far as i know, there is no way you can do it using Protractor. However, if you want to run the same suite twice, then there is a better way of handling this situation using data-providers. There are many ways to create a data driven framework for protractor, but the easiest one that i feel is using jasmine-data-provider, which is an npm package. Here's how you can do it -
Update conf.js file to include the suite and params -
suites: {
create_buyer: ['./firstSpec.js'] //All your specs
},
params: {
buyerName1: '',
buyerName2: ''
},
Update all test scripts file to include the dataprovider -
//Require the dataprovider
var dp = require('/PATH_TO/node_modules/jasmine-data-provider'); //Update your path where the jasmine-data-provider is installed
//Usage of dataprovider in your specs
var objectDataProvider = {
'Test1': {buyerName: browser.params.buyerName1},
'Test2': {buyerName: browser.params.buyerName2}
};
dp(objectDataProvider, function (data, description) {
//Whole describe or anything in the dp() runs twice
describe('First Suite Test: ', function(){
it('First Spec', function(){
element.sendKeys(data.buyerName); //usage of the data buyerNames
//Your code for the spec
});
//All your specs
});
});
Now pass in the parameters using command prompt -
protractor protractor.conf --suite create_buyer --params.buyerName1=Buyer1 --params.buyerName2=Buyer2
NOTE: However the issue here is that you cannot run one single suite at a single stretch with one buyerName. For ex: You cannot run all specs in the suite create_buyer at one stretch using buyerName1. Instead, one spec will run twice serially, once with buyerName1 and buyerName2, then it continues to next spec. But i guess, that should also work if your requirement is to not use a strict flow for one buyer (i.e, to complete end-to-end testing of suite create_buyer with buyerName1 and then run suite create_buyer with buyerName2 - this shouldn't be the case as the thumb rule of automation says that one test script shouldn't depend on another).
Hope it helps
Is it possible to leave the test browser windows open after Angular Protractor tests run? I have a tough test failing in FireFox and it'd be useful to access the state of the web page to see what's going on.
You can use Protractor debug/pause feature to pause the e2e run which will ultimately leave the browser open: more info here
To do so, add this line on your protractor test before the failing one
browser.pause();
There is also a very useful tool called elementor that you may want to take a look later on.
browser.pause no longer works with current Node v8.1.0, see here, but you could use browser.sleep(10000); to keep the browser open for e.g. 10 seconds
If you configured the test script to run using grunt, you could use the following code:
grunt.initConfig({
// ...
protractor: {
options: {
configFile: "protractor.conf.js",
keepAlive: true, // If false, the grunt process stops when the test fails.
noColor: false // If true, protractor will not use colors in its output.
},
run: {}
},
// ...
});
If you have Node 8+, bumped into issue "Error: Cannot find module '_debugger'" while attempting browser.pause solution from the accepted answer and you couldn't fix it using this github solution then you can workaround it as follows:
Install protractor as a module of the automation framework (i.e. without -g flag)
npm install protractor
Run webdriver-manager update for this protractor instance as well:
node ./node_modules/protractor/bin/webdriver-manager update
Where you have browser.pause(); in your code, replace it with debugger; statement
Run your code as follows:
node inspect ./node_modules/protractor/bin/protractor protractorConf.js
Where protractorConf.js is the config file of your protractor instance
If debugger waits for an input from you at the command line, just type cont and hit enter (to continue the execution)
super simple solution, that does exactly what's needed
The idea is that you don't really want to keep alive the session but rather pause it for a very long time when tests are done and be able to resume on-close procedures at any time
So just add this to your config.js
async onComplete() {
await browser.waitForAngularEnabled(false);
await browser.wait(
async () => {
let url = await browser.getCurrentUrl();
return url.includes('close/');
},
5 * 60 * 1000,
'Keep-alive timeout reached, closing the session...',
);
},
What the code does is, after all tests passed or failed, it waits until you type and submit close/ in browser's url field, or times out itself in 5 mins.
The reason await browser.waitForAngularEnabled(false); is needed here, because the browser opens an empty page when you type close/ which is non angular
You can even improve it and make it conditional, based on a parameter you pass to protractor config
I currently run my angularjs testing environment using ngMockEE which, using a special testing module allows me to mock http responses as such;
var things = [thing01, thing02];
$httpBackend.whenGET('/v1/things?').respond(function() {return [200, things]});
In a separate file I define e2e tests which call a things.html file that includes that testing module.
I then define a e2e test like this;
browser().navigateTo('/test/thingTest.html');
expect(element('.table.things tbody tr').count()).toEqual(2);
Now, what would be nice is if I could somehow couple the two togther, so that I could write an assertion like this - avoiding hard coding that 2 which will need updating over time if I update my list of things
browser().navigateTo('/test/thingTest.html');
expect(element('.table.things tbody tr').count()).toEqual(things.length);
However I'm not quite sure if this is possible or advisable. Any thoughts?
Well, you could use an e2eData.js file containing those values, and which would be loaded both by your tests, and by the tested page:
In e2eData.js:
...
var e2eData = {
things : [thing01, thing02],
...
}
In your e2e module:
$httpBackend.whenGET('/v1/things?').respond(function() {return [200, e2eData.things]});
In your test:
expect(element('.table.things tbody tr').count()).toEqual(e2eData.things.length);