Blog post on integration test (#247)

Adds new blog post about front-end integration testing and fixes logic in `fig.html` so alt tags are not overwritten
This commit is contained in:
Carol A. Scott 2019-04-11 16:29:19 -07:00 committed by GitHub
parent 2b8a0207d8
commit 281d41082e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 402 additions and 1 deletions

View File

@ -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:
<!-- markdownlint-disable MD013 -->
{{< 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" >}}
<!-- markdownlint-enable MD013 -->
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? Wed 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!**

View File

@ -145,5 +145,12 @@
"name": "Mohsen Rezaei",
"firstName": "Mohsen",
"lastName": "Rezaei"
},
"carol": {
"avatar": "",
"email": "carol@buoyant.io",
"name": "Carol Scott",
"firstName": "Carol",
"lastName": "Scott"
}
}

View File

@ -1,4 +1,4 @@
{{ $alt := default (.Get "alt") (.Get "title") }}
{{ $alt := (.Get "alt") | default (.Get "title") }}
<figure class="figure w-100 text-center my-4">
<a href="{{ .Get "src" }}" data-toggle="lightbox">
<img src="{{ .Get "src" }}"

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB