Skip to content

Instantly share code, notes, and snippets.

@klamping
Forked from jrobinson01/wdio-best-practices.md
Last active February 27, 2024 08:25
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save klamping/72b7e78e6edf1d2be8363af415fe0c98 to your computer and use it in GitHub Desktop.
Save klamping/72b7e78e6edf1d2be8363af415fe0c98 to your computer and use it in GitHub Desktop.
WDIO testing best practices

WDIO Testing Best Practices

  • Use page objects
  • avoid protocol methods and prefer commands
  • don't use timeouts unless you have a good reason
  • use waitFor, waitForVisible etc
  • avoid caching elements
  • avoid arbitrary pause() calls
  • use mocks

Protocol commands

Protocol commands bypass nifty features of webdriverio. They're also painful to look at. If you must use them, do it in a page object.

(Maybe add an example here of what a protocol method is versus a webdriverio command. Something like 'click')

Page objects

The page-object pattern is a useful abstraction to separate the ugly implementation details from the assertions your tests need to make. Page objects are a lot like a service layer. They're responsible for querying the DOM for elements, filling out forms, waiting for things, etc. This gives your test specs a clean api to work with, and can stay focused on making assertions. Page objects are also useful in that they can be reused in multiple tests.

timeouts

Unless something typically takes LONGER than the default timeout (currently, 30s), avoid using timeouts. It won't make your tests any faster, but will allow them to fail falsely more often. If you must use a timeout, try to do it in a page-object instead of your .spec files.

caching elements

Caching elements leads to stale element references. It also dirties up your test code with utility methods. Instead, use getters in page objects so when you interact with an element, you're always interacting with an "as up-to-date as possible" element.

bad:

function getButton() {
  return browser.$('button');
}

//.. later
it('button is enabled', () => {
  expect(button.isEnabled()).toBe(true);
})

better:

// pageObject.js
get button() {
  return browser.$('button');
}
// test.spec.js
it('button is enabled', () => {
  expect(pageObject.button.isEnabled()).toBe(true);
});

Avoid browser.pause()

Avoid calling browser.pause(randomNumber) in your test code. Pause is useful in that it's very easy to use, but it's also confusing when used in .spec files. Instead, wrap it in a page-object method like so:

// pageObject.js
waitForDialog() {
  browser.pause(DIALOG_ANIMATION_TIME);
}
// test.spec.js
it('should close the dialog', () => {
  page.someButton.click();
  page.waitForDialog();
  // assert something
})

By using it this way, the pause timeout value is localized in your page object and the waitForDialog method name gives hints as to what is going on.

Use mocks

Mocks are incredibly important. Mocking API responses gives you full control over timing. Without this control, you give up a lot of stability in your tests and are at the mercy of network latency. Testing against live API's is important but that should be reserved for "live" or "user" tests.

Gotchas

Jasmine (currently) will swallow errors in beforeAll and afterAll and afterEach. Wrap contents of those methods in try catch.

bad:

beforeAll(() => {
  pageObject.open();
});

better:

beforeAll(() => {
  try {
    pageObject.open();
  } catch(e) {
    console.error('error in beforeAll', e);
    throw e;
  }
});

Shadow DOM

TODO: ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment