C0fc0b68c2d4a269629190b963b8300e?s=70x70

When it was announced that Protractor would be replacing the karma runner as THE end to end test framework for AngularJS, I’ll admit, I was a bit sad. While I didn’t have a deep love for the karma runner, it was crazy simple to setup and run, and my initial perusal of the Protractor docs was intimidating. After spending some time with Protractor though, I have come to enjoy using it in the same manner in which I would test a Rails application.

Going through the Protractor docs and getting started can still be a daunting task. My goal is to alleviate some of that by sharing my configuration and setup, as well as using the page object pattern for creating an API through which we’ll interact with our web pages. We’ll also be using the jasmine-given library to help clean up our specs.

The Setup

The first thing we will need to do is make sure we have Protractor and jasmine-given available to our application.

$ npm install protractor jasmine-give --save-dev

Please note the --save-dev. This is so that node will add these as dev dependencies to package.json. It is also important to note that Protractor can be installed globally with the -g switch. Personally, I do not like to install application dependencies globally, and try to avoid them if at all possible.

Protractor has two dependencie of it’s own, selenium server and a browser driver. There exists pages of documentation for running the selenium server stand alone and managing browser drivers, but Protractor gives us a script to manage both.

In your project directory, run the following command:

$ node_modules/protractor/bin/webdriver-manager update

This will install the selenium server jar file and the chrome driver executable locally to your project. Now to start the selenium server, simply run the following command in a separate terminal window.

$ node_modules/protractor/bin/webdriver-manager start

The last piece of our setup puzzle is the config file that we’ll pass to Protractor when running our specs.

In spec-e2e.conf.js

exports.config = {
  seleniumAddress: "http://127.0.0.1:4444/wd/hub",
  seleniumPort: null,
  seleniumArgs: [],
  specs: [
    '../spec-e2e/**/*spec.{js,coffee}'
  ],
  capabilities: {
    'browserName': 'chrome'
  },
  baseUrl: 'http://localhost:8000',
  jasmineNodeOpts: {
    onComplete: null,
    isVerbose: false,
    showColors: true,
    includeStackTrace: false
  }
};

With that, we tell protractor where to find our selenium server, which browser(s) to test against, and any jasmine options we choose to pass along. You may reference the example config in the Protractor repo for additional config options.

Our first test

For a simple example of using Protractor to test drive an application, we are going to use logging into a web application. In spec/e2e/login_spec.js we setup our test using jasmine-given and page objects.

require("protractor/jasminewd");
require('jasmine-given');

var LoginPage = require("./pages/login_page");

describe("app", function() {
  var page = new LoginPage();
  describe("visiting the login page", function() {
    Given(function() {
      page.visitPage();
    });
    describe("when a user logs in", function() {
      Given(function() {
        page.fillEmail("testy@example.com");
      });
      Given(function() {
        page.fillPassword();
      });
      When(function() {
        page.login();
      });
      Then(function() {
        page.getCurrentUser().then(function(text) {
       expect(text).toEqual("Randy Savage");
        });
      });
    });
  });
});

First we require our protractor jasmine web driver, and also jasmine-given. Next we require our not yet existing login page object. Last, we walk through the steps of navigating to the login page, filling in and submitting the form. Last, we assert that our page knows about our currentUser and that it’s text is equal to Randy Savage.

We can run this spec with the following command:

$ node_modules/protractor/bin/protractor path/to/spec-e2e.conf.js

The output from this run should throw an error that node cannot find the module pages/login_page, so let’s go ahead and add that.

var LoginPage = (function () {
    function LoginPage() {
        this.emailField = element(By.input("user.email"));
        this.passwordField = element(By.input("user.password"));
        this.loginButton = element(By.id("log-in"));
        this.currentUser = element(By.binding("{{currentUser.name}}"));
    }

    LoginPage.prototype.visitPage = function () {
        browser.get("/");
    };

    LoginPage.prototype.fillEmail = function (email) {
        this.emailField.sendKeys(email);
    };

    LoginPage.prototype.fillPassword = function (password) {
        if (password == null) {
            password = "password";
        }
        this.passwordField.sendKeys(password);
    };

    LoginPage.prototype.login = function () {
        this.loginButton.click();
    };

    LoginPage.prototype.getCurrentUser = function () {
        return this.currentUser.getText();
    };

    return LoginPage;

})();

module.exports = LoginPage;

In the constructor function of our LoginPage class, we setup instances of the things on the page we wish to interact with using Protractor’s locators. We use By.input to find an input with a binding to ng-model="user.email" and again for ng-model="user.password". We find our submit button by id, and lastly we find the current users name by searching the page for the currentUser.name binding. We then setup the functions on our page object that we are calling from our spec, using the elements we setup in our constructor. It is important to note the implicit return of this.currentUser.getText(). This is because the getText() method returns a promise, which we return to and resolve in our spec. Finally we ensure that our page object is available to the spec by exporting it.

Now, when we run our spec again, we should get an actionable failure Error: No element found using locator: by.model("user.email") allowing us to implement our markup and logic to get our spec passing. With just a few simple steps, we were able to get a selenium server running and Protractor configured to use it. As well as creating a pleasant testing DSL by abstracting our page specific login into a reuseable page object.

1af30891b7e22f41acf79a1e103b6a18?s=70x70

rails conf

Abstract

When our tests fail all we get is output in a terminal. Testing is core to the culture of Ruby, and the tools are sophisticated, but something big is missing from the workflow. Namely, a powerful debugger.

Integrating Pry creates an interactive, enjoyable TDD workflow. In this talk, I’ll recount the somewhat unique development experience that made me a believer. I’ll demonstrate with Pry a responsive and immersive TDD cycle, taking a test to green before it exits its first run.

Details

Best case scenario, the audience understands the value of making a debugger like Pry an integral part of their workflow rather than an afterthought. I want to show people how enjoyable an interactive TDD workflow can be. I want people to become fans and supporters of Pry.

I want to demonstrate with examples the pain of how most Rubyists TDD, which is mostly about being in the dark. I want to contrast that with a workflow that integrates Pry, which is about being immersed in the system while testing it and implementing it, and getting much faster feedback. I am considering demoing the TDD lifecycle in Smalltalk. Whether I do or not, I want to show how a debugger drives and informs the TDD process.

Pitch

I am a TDD believer and have been practicing it for many years in various languages.

I don’t have much involvement in Pry other than being a fan. I have been promoting it on the Gaslight blog over the years. I Like Pry But…, Pryme Time.

I saw Conrad Irwin’s talk at Rails Conf in Portland and it was vey well received. I consider this follow up. An intermediate and practical application of Pry as part of a regular TDD workflow.

The unique development experience that I allude to in my abstract is that I was a professional Smalltalk developer for some years. Like many Rubyists, I’m skeptical of IDEs and I’m most productive with a terminal and VIM. But I also loved working in the Smalltalk environment, which is arguably the worlds most unforgiving IDE. That enjoyment had a lot to much do with an integrated, interactive debugging tool that drove that process. I’ve never encountered anything quite like it in any other language.

Speakers

Joel Turnbull (joelbywan@gmail.com) Joel Turnbull is a Code Designer at Gaslight. He’s been a staple in the Cincinnati development community for over 10 years. This is his first presentation at a Rails Conf.