Protractor is an end-to-end testing framework specifically designed for Angular applications. It interacts with your application like a real user would, simulating user interactions such as clicking, typing, and navigation. Protractor is built on top of WebDriverJS, and it integrates with Jasmine for test assertions, which is commonly used in AngularJS applications.
This guide will walk you through setting up Protractor, writing basic end-to-end tests, and executing them for your AngularJS application.
1. Setting Up Protractor
Step 1: Install Protractor
To get started with Protractor, you need to install it along with the necessary dependencies. First, you need to install Node.js if it’s not already installed. Then, install Protractor globally using npm.
npm install -g protractor
This will install Protractor globally on your machine. You can verify the installation with the following command:
protractor --version
Step 2: Install WebDriver
Protractor requires WebDriver for interacting with browsers. You can install WebDriver using the following command:
webdriver-manager update
This command downloads the necessary binaries for different browsers (e.g., ChromeDriver, GeckoDriver for Firefox).
To start the WebDriver server, run:
webdriver-manager start
Now, Protractor will be able to control your browser for end-to-end testing.
Step 3: Configure Protractor
Create a configuration file for Protractor, typically named protractor.conf.js
. This file defines how Protractor runs the tests, which browser to use, the test framework, and other settings.
Here’s an example protractor.conf.js
:
exports.config = {
directConnect: true, // Direct connection to the browser without Selenium Server
capabilities: {
browserName: 'chrome' // You can use chrome or firefox here
},
framework: 'jasmine', // We use Jasmine for the test framework
specs: ['./tests/spec.js'], // Path to your test files
jasmineNodeOpts: {
defaultTimeoutInterval: 30000, // Time limit for each test
showColors: true, // Display results in color
},
onPrepare: function() {
// This can be used to configure any setup before tests run
browser.ignoreSynchronization = true; // Ignore AngularJS synchronization (if necessary)
}
};
This configuration file tells Protractor to use the Jasmine framework, the Chrome browser, and the path to the test files. The onPrepare
function is useful for performing setup before tests run (e.g., adjusting AngularJS synchronization).
2. Writing Your First Protractor Test
Now that Protractor is set up, let’s write our first end-to-end test for an AngularJS application.
Step 1: Create a Test File
In the specs
directory (or another folder), create a file called spec.js
. This is where you’ll write your test scripts.
Step 2: Write a Basic Test
Here’s a simple test case that checks if the title of the AngularJS application is correct:
describe('AngularJS Application', function() {
// Navigate to the application's home page
it('should have the correct title', function() {
browser.get('http://localhost:8080'); // URL of the application
// Get the title of the page and verify it
expect(browser.getTitle()).toBe('My AngularJS App');
});
});
Step 3: Running the Test
Once your test is written, you can run it using the following command:
protractor protractor.conf.js
Protractor will:
- Launch the browser (Chrome, in this case).
- Navigate to
http://localhost:8080
(your app’s URL). - Check the title of the page and verify if it matches “My AngularJS App”.
3. Simulating User Interactions
Protractor allows you to simulate various user actions like clicking buttons, entering text in input fields, and more. Here’s an example of how to test a login form.
Step 1: Write a Test for a Form
Let’s assume the application has a login form with the following HTML:
<form name="loginForm">
<input type="text" ng-model="username" name="username" />
<input type="password" ng-model="password" name="password" />
<button ng-click="login()" type="submit">Login</button>
</form>
Here’s how to write a test for this form:
describe('Login Form', function() {
it('should submit the form when valid data is entered', function() {
// Open the login page
browser.get('http://localhost:8080/login');
// Fill in the username and password fields
element(by.model('username')).sendKeys('testuser');
element(by.model('password')).sendKeys('password123');
// Click the login button
element(by.buttonText('Login')).click();
// Wait for the next page to load and check the URL
browser.wait(protractor.ExpectedConditions.urlContains('/dashboard'), 5000);
// Verify that the URL contains '/dashboard'
expect(browser.getCurrentUrl()).toContain('/dashboard');
});
});
Step 2: Running the Test
After writing the test, run it with the following command:
protractor protractor.conf.js
In this test:
element(by.model('username'))
: Locates the input field for the username using the AngularJS model binding.sendKeys()
: Enters text into the input field.element(by.buttonText('Login')).click()
: Clicks the login button.browser.wait()
: Waits for the URL to contain/dashboard
after login, ensuring the login was successful.expect()
: Verifies that the current URL contains/dashboard
.
4. Handling AngularJS Specific Features
AngularJS applications often include features like two-way data binding and routing, which can make tests a bit more complicated. Protractor provides a couple of options to handle Angular-specific features.
Handling Angular Synchronization
Protractor automatically waits for AngularJS to complete its tasks (e.g., updating the view, finishing an AJAX call). However, if you’re testing non-Angular pages or performing actions outside of Angular’s control, you may need to disable synchronization.
For non-Angular pages or when needed, set the ignoreSynchronization
option to true
:
browser.ignoreSynchronization = true;
This tells Protractor not to wait for AngularJS to finish before performing actions.
Handling Delays with ExpectedConditions
Protractor provides ExpectedConditions
for waiting for certain conditions to be true, such as waiting for an element to be visible or clickable.
var EC = protractor.ExpectedConditions;
var loginButton = element(by.buttonText('Login'));
// Wait until the login button is clickable
browser.wait(EC.elementToBeClickable(loginButton), 5000);
// Click the login button
loginButton.click();
5. Using Protractor with Jasmine Assertions
Protractor uses Jasmine for its assertion framework. You can use all of Jasmine’s built-in matchers, like expect()
, to assert conditions.
expect(element.isPresent()).toBe(true)
: Verifies that an element is present in the DOM.expect(element.getText()).toEqual('Expected Text')
: Verifies that the text of an element matches the expected text.expect(browser.getCurrentUrl()).toContain('some/path')
: Verifies that the current URL contains a specific string.
6. Debugging Tests
Sometimes your tests may not behave as expected. Here are a few strategies for debugging:
- Add a
browser.sleep()
: Temporarily pause the test to inspect the page manually.browser.sleep(3000); // Wait for 3 seconds
- Use
console.log()
: Print out values in your test for inspection. - Protractor Debugging: Run Protractor with the
--debug
flag to step through your test in the Node.js debugger.
7. Running Tests in Continuous Integration
For CI (Continuous Integration) pipelines, you can run Protractor tests in headless mode (without opening a browser window) using tools like Chrome in headless mode or using a service like Sauce Labs for cloud testing.
Add the following to your protractor.conf.js
for headless testing:
capabilities: {
browserName: 'chrome',
chromeOptions: {
args: ['--headless', '--disable-gpu', '--window-size=800x600']
}
}