diff --git a/linkerd.io/content/blog/browser-testing-from-scratch-building-quick-and-easy-integration-tests-with-webdriverio-and-saucelabs.md b/linkerd.io/content/blog/browser-testing-from-scratch-building-quick-and-easy-integration-tests-with-webdriverio-and-saucelabs.md new file mode 100644 index 00000000..244891a0 --- /dev/null +++ b/linkerd.io/content/blog/browser-testing-from-scratch-building-quick-and-easy-integration-tests-with-webdriverio-and-saucelabs.md @@ -0,0 +1,394 @@ +--- +title: 'Browser Testing from Scratch: Building Quick and Easy Integration Tests +with WebdriverIO and SauceLabs' +author: 'carol' +thumbnail: linkerd-dashboard.png +date: 2019-04-10T10:12:45-07:00 +draft: false +tags: [Linkerd, linkerd, News, tutorials] +--- + +*This post was written for engineers who are building front-end +integration test suites from scratch, particularly if your project is +open-source. If you want to skip the background information and go right to the +tutorial, scroll down to* **"Building Your Testing Architecture."** + +One of the coolest things about working on [Linkerd](http://www.linkerd.io) is +how excited our users are about our clean, deceptively simple dashboard, built +with [React](https://reactjs.org/) and [Material-UI](https://material-ui.com/). +(Wanna try it? Get our super-light, open-source service mesh [up and running in +just a few minutes](https://linkerd.io/2/getting-started/)!) + +{{< fig + alt="Linkerd dashboard screenshot from edge release 19.3.2" + title="Linkerd dashboard screenshot from edge release 19.3.2." + src="/uploads/2019/04/linkerd-dashboard-screenshot.png" >}} + +And when I say excited, I mean unsolicited-praise excited: we constantly get +messages from users like this: + + +{{< fig + alt="From @stephenpope on Twitter: Very pleased with #Linkerd2 - deployed my app (with auto-proxy-injection) and #itjustworked - Had all the info I needed on the dashboard - Thanks very much (great docs too)" + title="Tweet by a happy Linkerd user!" + src="/uploads/2019/04/happy-tweet.png" >}} + + +We want to keep our users happy with a clean, consistent dashboard as we +constantly roll out new features and improvements, so recently we built a suite +of integration tests simulating a user's traversal through the application. + +## Why Front-End Integration Tests? + +If you're reading this, you're probably already sold on the idea of testing, but +as an [open source project](https://www.cncf.io/project-faq/linkerd/), there are +a few additional advantages of front-end integration tests you may not have +considered: + +### Integration tests are a great way to onboard open source contributors + +We are lucky to have a ton of contributors to Linkerd, both power users and open +source enthusiasts. Forking our repo and running the tests are a great way for a +new contributor to ensure their development environment is properly set up. + +### ...And to encourage them to submit their first PR + +Writing, or updating, a test is a fabulous way to contribute to a project +without much prior knowledge required. + +### Most importantly, you can sanity-check UI changes in a fast-moving dev environment + +If you're reviewing multiple PRs a day from many different contributors, it +becomes important to have a set of actions to run through in the browser to +ensure you haven't inadvertently broken or changed any functionality. + +Yes, clicking through yourself is always the first step when making a front-end +change! But, I mean, we're not *robots*. That's where WebdriverIO comes in: + +{{< fig + alt="WebdriverIO logo" + title="WebdriverIO logo." + src="/uploads/2019/04/webdriverio-logo.png" >}} + +WebdriverIO is a test automation framework for Node.js that allows you to get a +test suite up and running in minutes. You don't need any Selenium knowledge to +get started, although there is an entire universe of libraries built by the +WebdriverIO community that can add layers of complexity. + +In the spirit of open source, we're sharing what we've learned. This post should +take you from 0 to a full test architecture. You'll be able to run your tests +locally and, if you have a Sauce Labs account, in the cloud! SauceLabs is free +for open source projects. Let's get started! + +# Building Your Testing Architecture + +Before we start, you need to have the following installed: + +- [Yarn](https://yarnpkg.com/en/docs/getting-started) +- [Node.js](https://nodejs.org/en/download/) + +**Recommended**: If you install Yarn through the [Homebrew package +manager](https://brew.sh/) it will download Node.js as well. + +First, navigate to the directory in your project that holds your web +application. Ours is named `app`: + +```bash +cd app +``` + +Now it's time to add some packages! First, we'll add WebdriverIO: + +```bash +yarn add webdriverio --dev +``` + +We added the `--dev` flag because we want this package to be part of the +`devDependencies` portion of our `package.json`, since we'll be running our +tests in a development environment. If you didn't already have a `package.json` +file, this command will create one for you. + +Now, we'll add WebdriverIO's testrunner command line interface, which is what +will allow you to run the tests from your terminal: + +```bash +yarn add @wdio/cli --dev +``` + +{{< note >}} +When WebdriverIO 5 was released, the project switched to a `@wdio` +naming structure. In general, if a library is prefixed with `@wdio` it is +officially part of the WebdriverIO project, and if it is prefixed with `wdio` it +is a community project. +{{< /note >}} + +The `@wdio/cli` testrunner comes with a config file generator, which we won't +use for this project. If you want to learn more about it, visit WebdriverIO's +[Getting Started](https://webdriver.io/docs/gettingstarted.html) page. Instead, +we'll download the packages we need directly: + +```bash +yarn add chromedriver @wdio/local-runner @wdio/mocha-framework @wdio/sync wdio-chromedriver-service --dev +``` + +These packages are, respectively: + +- `chromedriver` Automated testing for Chrome +- `@wdio/local-runner` A WebdriverIO runner to run tests locally +- `@wdio/mocha-framework` Adapter for Mocha testing framework +- `@wdio/sync` Helper module to run WebdriverIO commands synchronously +- `wdio-chromedriver-service` (community package: WebdriverIO service to start & + stop ChromeDriver) + +Now, from `app`, let's create a subdirectory for our tests: + +```bash +mkdir integration && cd integration +touch wdio.conf.js +``` + +In the `integration` directory, we'll create a config file to tell WebdriverIO +what to do. In your text editor, open `wdio.conf.js` and paste: + +```javascript +exports.config = { + port: 9515, // default for ChromeDriver + path: '/', + services: ['chromedriver'], + runner: 'local', + specs: [ + './integration/specs/*.js' + ], + exclude: [ + // 'path/to/excluded/files' + ], + maxInstances: 10, + capabilities: [{browserName: 'chrome', platform: 'OS X 10.13', version: '69.0'}], + bail: 0, + baseUrl: 'http://localhost', + waitforTimeout: 10000, + connectionRetryTimeout: 90000, + connectionRetryCount: 3, + framework: 'mocha', + mochaOpts: { + ui: 'bdd', + timeout: 60000 + } +} +``` + +As you can see, we're specifying that our tests will live in +`/integration/specs/`, that we are using Chromedriver and specifying a specific +platform and version to run the tests. + +Create a `specs/` directory with a sample test file: + +```bash +mkdir specs && cd specs && touch first-test.js +``` + +At this point your directory structure should look like this: + +```bash +app/ + node_modules/ + package.json + integration/ + wdio.conf.js + specs/ + first-test.js +``` + +Let's run a simple test where we go over to [Linkerd.io](http://linkerd.io) and +check that the title of the page is what we expect. Open `first-test.js` and +paste the following: + +```javascript +const assert = require('assert'); +describe('logo link test', function() { + it('should redirect to the home view if logo is clicked', () => { + browser.url('http://www.linkerd.io'); + const title = browser.getTitle(); + assert.equal(title, 'Linkerd - Linkerd'); + }); +}); +``` + +Right now, you're still in your `specs` directory. Go back up to your `app` +directory to run the tests: + +```bash +cd ../.. +./node_modules/.bin/wdio ./integration/wdio.conf.js +``` + +You should see a message in your terminal saying "Starting ChromeDriver on port +9515". An instance of Chrome window should then open up, go to +[Linkerd.io](http://linkerd.io) and close. You should see "1 Passing" in your +terminal! + +{{< fig + alt="WebdriverIO success message in terminal: 1 passed, 1 total (100% completed)" + title="WebdriverIO terminal output." + src="/uploads/2019/04/terminal-message.png" >}} + +Awesome, you just successfully ran an integration test with WebdriverIO! 🥳 You +can start building these tests out to test your application. I've found the +WebdriverIO [Selector Docs](https://webdriver.io/docs/selectors.html) very +helpful. + +# Testing in the Cloud + +You're currently just running the tests on one browser. This will help find bugs +in functionality, but a big part of front-end development is making sure your +application looks consistent — and works — on multiple browsers and devices. + +You could instruct WebdriverIO to run the tests on multiple different browsers +locally (and maybe you want to), but if you add in all of the different versions +and device sizes that your application should support, that quickly grows out of +hand. + +Enter SauceLabs, whose cross-browser testing tools are [free for open source +projects](https://saucelabs.com/open-source) and allow you to run tests in a +host of browser types, versions and sizes. + +{{< fig + alt="SauceLabs logo" + title="SauceLabs logo." + src="/uploads/2019/04/saucelabs-logo.png" >}} + +If you have an open source project and want to use SauceLabs, you first need to +[apply for an account](https://saucelabs.com/open-source). You can also sign up +for a free trial on their site. (If you're a Linkerd contributor, the best way +to get a contributor credential is to [ask on our Slack +channel](https://slack.linkerd.io/).) + +SauceLabs will give you a username and key. Once you have those credentials, set +them as permanent environment variables. This keeps your credentials private, +and means that everyone on the team can run the tests via their unique login +without modifying the test files. + +To do that, open your `~/.bash_profile` file and add: + +```bash +export SAUCE_USERNAME="your Sauce username" +export SAUCE_ACCESS_KEY="your Sauce access key" +``` + +Great! Now, in your `integration` directory, we'll need to create a separate +config file for running WebdriverIO with SauceLabs. + +```bash +cd integration # from app directory +touch wdio-sauce.conf.js +``` + +Open that file and paste in the following: + +```javascript +exports.config = { + runner: 'local', + user: process.env.SAUCE_USERNAME, + key: process.env.SAUCE_ACCESS_KEY, + sauceConnect: true, + specs: [ + './integration/specs/*.js' + ], + // Patterns to exclude. + exclude: [ + // 'path/to/excluded/files' + ], + maxInstances: 10, + capabilities: [ + {browserName: 'firefox', platform: 'Windows 10', version: '60.0'}, + {browserName: 'chrome', platform: 'OS X 10.13', version: '69.0'} + ], + bail: 0, + baseUrl: 'http://localhost', + waitforTimeout: 10000, + connectionRetryTimeout: 90000, + connectionRetryCount: 3, + framework: 'mocha', + mochaOpts: { + ui: 'bdd', + timeout: 60000 + } +} +``` + +As you can see, we've removed the `port` , `path` and `services` variables and +added our Sauce `user` and `key`. We've switched on something called +`sauceConnect` and added two specific browsers to try, Firefox and Chrome. + +# Tunneling to Localhost in 3, 2, 1 + +[SauceConnect](https://wiki.saucelabs.com/display/DOCS/Sauce+Connect+Proxy) is a +proxy server that will open a tunnel between a SauceLabs Virtual Machine (VM) +and whatever you are running on `localhost.`You'll want to save it in a separate +directory from the rest of your development files. + +{{< note >}} +SauceConnect only [supports specific port numbers to tunnel to +localhost](https://wiki.saucelabs.com/display/DOCS/Sauce+Connect+Proxy+FAQS#SauceConnectProxyFAQS-CanIAccessApplicationsonlocalhost), +so you may need to change the port where you serve the dashboard. We had to +change from `:8084` to `:7777` in order for the tunnel to work. +{{< /note >}} + +After [downloading +SauceConnect](https://wiki.saucelabs.com/display/DOCS/Sauce+Connect+Proxy), +navigate to that directory and start it up: + +```bash +SC=sc-4.5.3-osx # OSX example +wget -O - https://saucelabs.com/downloads/$SC.zip | tar xfz - -C ~/ +cd ~/$SC +bin/sc -u $SAUCE_USERNAME -k $SAUCE_ACCESS_KEY +``` + +Wait until you see "Sauce Connect is up, you may start your tests" in your +terminal. Open a separate terminal window, navigate to `app` and run: + +```bash +./node_modules/.bin/wdio ./integration/wdio-sauce.conf.js +``` + +SauceLabs will start running the tests in the cloud. You can watch them run in +real-time from the [SauceLabs +dashboard](https://app.saucelabs.com/dashboard/tests) (and even take over if you +want to manually control where the test goes). + +{{< fig + alt="SauceLabs dashboard screenshot showing a report of an integration test" + title="SauceLabs dashboard screenshot." + src="/uploads/2019/04/saucelabs-dashboard-screenshot.png" >}} + +If any tests fail, you'll immediately get the URL in your terminal window with a +video of the test and information about what happened. (Break the test and try +that now!) + +When you're finished testing, **close the tunnel** by pressing `CTRL-C` in the +Sauce Connect window. If you forget to do this, it will close on its own after a +few minutes. + +# Finishing Up + +Congratulations! You've now set up testing architecture for your project, with a +test you can run locally on Chrome and in the cloud on multiple browsers. + +If you want to make it REALLY easy for your team to run, you can create +shortcuts in a `bin` file (check out the `integration` function [in our repo's +bin/web file](https://github.com/linkerd/linkerd2/blob/master/bin/web)). + +# Go Forth and Test! 🚀 + +Your next challenge is to replace our simple `first-test.js` with a set of +practical tests for your own application. Good luck! If you have any questions, +feel free to shoot me [a note on Twitter](https://twitter.com/_carolscott) or +[file an issue or feature request for +Linkerd](https://github.com/linkerd/linkerd2). + +**Want to learn more? We’d love to have you join our rapidly-growing community! +Linkerd is [hosted on GitHub](https://github.com/linkerd/linkerd2), and we have +a thriving community on [Slack](https://slack.linkerd.io), +[Twitter](https://twitter.com/linkerd), and our [mailing +lists](https://lists.cncf.io/g/cncf-linkerd-users). Come and join the fun!** diff --git a/linkerd.io/data/authors.json b/linkerd.io/data/authors.json index 6e01ab4d..5d45a53b 100644 --- a/linkerd.io/data/authors.json +++ b/linkerd.io/data/authors.json @@ -145,5 +145,12 @@ "name": "Mohsen Rezaei", "firstName": "Mohsen", "lastName": "Rezaei" + }, + "carol": { + "avatar": "", + "email": "carol@buoyant.io", + "name": "Carol Scott", + "firstName": "Carol", + "lastName": "Scott" } } diff --git a/linkerd.io/layouts/shortcodes/fig.html b/linkerd.io/layouts/shortcodes/fig.html index a555a220..2de4aa87 100644 --- a/linkerd.io/layouts/shortcodes/fig.html +++ b/linkerd.io/layouts/shortcodes/fig.html @@ -1,4 +1,4 @@ -{{ $alt := default (.Get "alt") (.Get "title") }} +{{ $alt := (.Get "alt") | default (.Get "title") }}