Appium Test Support Utilities
raw JSON →`appium-test-support` is a collection of JavaScript test utilities designed for internal use across various Appium packages. It provides common testing patterns such as environment variable stubbing (`stubEnv`), log output capturing and manipulation (`stubLog`), and advanced Sinon-based testing with sandboxes and mocks (`withSandbox`, `withMocks`, `fakeTime`). The current stable version is 1.3.3. As an internal utility library, its release cadence is tied to the broader Appium project's needs and updates, typically seeing updates in line with major Appium releases or when internal testing needs evolve. It differentiates itself by offering pre-packaged solutions for common Appium testing scenarios, including specific support for Android emulator setup on Travis CI, streamlining testing workflows within the Appium ecosystem and reducing boilerplate in Appium's own tests.
Common errors
error TypeError: Cannot read properties of undefined (reading 'expects') ↓
mocks configuration object passed to withMocks or withSandbox and accessed via the provided mocks or S.mocks parameter, e.g., withMocks({ myApi }, (mocks) => { mocks.myApi.expects(...) });. error Error: Sandbox not found ↓
sinon.createSandbox() is called in a beforeEach hook and the resulting sandbox instance is passed to fakeTime. Also, always call sandbox.restore() in an afterEach hook to clean up. error Error: 'console.error' was replaced, but unable to restore. ↓
process.env or console within the same test context. Ensure stubEnv is used within a clean context where its internal restoration mechanism can operate without interference. Warnings
breaking Upgrades to underlying testing libraries like Sinon.js (if they are direct dependencies of `appium-test-support` or peer dependencies of your project) can introduce breaking changes to how stubs, mocks, and fake timers behave. Always consult the release notes of `sinon` when upgrading your testing dependencies. ↓
gotcha Failure to properly manage Sinon sandboxes (e.g., calling `sandbox.restore()` in `afterEach`) can lead to test pollution, where stubs and mocks from one test leak into subsequent tests, causing unpredictable and flaky failures. This applies to `withSandbox`, `withMocks`, and `fakeTime`. ↓
gotcha `stubEnv` only affects `process.env` within the current test context and ensures it's restored after the test. Misunderstanding its scope can lead to confusion if expecting global or persistent changes, or if used in an environment where `process.env` might be mutated by other means outside the library's control. ↓
gotcha The `stripColors` option in `stubLog` specifically targets common ANSI color codes. If your logging library uses non-standard color representations or custom formatting, `stripColors: true` might not produce the expected plain text output, potentially leaving unexpected characters in your log assertions. ↓
Install
npm install appium-test-support yarn add appium-test-support pnpm add appium-test-support Imports
- stubEnv wrong
const stubEnv = require('appium-test-support').stubEnv;correctimport { stubEnv } from 'appium-test-support'; - stubLog wrong
import stubLog from 'appium-test-support/stubLog';correctimport { stubLog } from 'appium-test-support'; - withSandbox wrong
const { withSandbox } = require('appium-test-support');correctimport { withSandbox } from 'appium-test-support'; - fakeTime wrong
import * as fakeTime from 'appium-test-support/fakeTime';correctimport { fakeTime } from 'appium-test-support';
Quickstart
import { fakeTime } from 'appium-test-support';
import sinon from 'sinon'; // Requires 'sinon' as a dev dependency
import { expect } from 'chai'; // Requires 'chai' for assertions
// Simplified Promise-like object for demonstration if Bluebird isn't available
class CustomPromise {
constructor(executor) {
this.resolve = null;
this.reject = null;
const promise = new Promise((res, rej) => {
this.resolve = res;
this.reject = rej;
});
executor(this.resolve, this.reject);
return promise;
}
}
function doSomethingThatTakesTime() {
return new CustomPromise((resolve) => {
let ret = '';
function appendOneByOne () {
if(ret.length >= 10) {
return resolve(ret);
}
setTimeout(() => {
ret = ret + ret.length;
appendOneByOne();
}, 1000);
}
appendOneByOne();
});
}
describe('fakeTime demonstration', () => {
let sandbox: sinon.SinonSandbox;
beforeEach(() => {
sandbox = sinon.createSandbox();
});
afterEach(() => {
sandbox.restore();
});
it('should fast-forward time to test async operations', async () => {
const timeLord = fakeTime(sandbox);
const p = doSomethingThatTakesTime();
// Simulate 60 intervals of 200ms each, effectively advancing 12000ms (12 seconds)
// This allows the `doSomethingThatTakesTime` function to complete its 10 * 1000ms operations.
timeLord.speedup(200, 60);
expect(await p).to.equal('0123456789');
});
});