We've looked at and written a few specs through the examples already. Now it's time to take a closer look at the spec framework itself. How exactly do you write tests in Atom?
Atom uses Jasmine as its spec framework. Any new functionality should have specs to guard against regressions.
Spec files must end with
-spec so add
sample-spec.coffee to the
describe method takes two arguments, a description and a function. If the description explains a behavior it typically begins with
when; if it is more like a unit test it begins with the method name.
describe "when a test is written", -> # contents
describe "Editor::moveUp", -> # contents
it method also takes two arguments, a description and a function. Try and make the description flow with the
it method. For example, a description of "this should work" doesn't read well as "it this should work". But a description of "should work" sounds great as "it should work".
describe "when a test is written", -> it "has some expectations that should pass", -> # Expectations
The best way to learn about expectations is to read the Jasmine documentation about them. Below is a simple example.
describe "when a test is written", -> it "has some expectations that should pass", -> expect("apples").toEqual("apples") expect("oranges").not.toEqual("apples")
In addition to the Jasmine's built-in matchers, Atom includes the following:
toBeInstanceOfmatcher is for the
toHaveLengthmatcher compares against the
toExistOnDiskmatcher checks if the file exists in the filesystem
toHaveFocusmatcher checks if the element currently has focus
toShowmatcher tests if the element is visible in the dom
These are defined in spec/spec-helper.coffee.
Writing Asynchronous specs can be tricky at first. Some examples.
Working with promises is rather easy in Atom. You can use our
describe "when we open a file", -> it "should be opened in an editor", -> waitsForPromise -> atom.workspace.open('c.coffee').then (editor) -> expect(editor.getPath()).toContain 'c.coffee'
This method can be used in the
describe "when we open a file", -> beforeEach -> waitsForPromise -> atom.workspace.open 'c.coffee' it "should be opened in an editor", -> expect(atom.workspace.getActiveTextEditor().getPath()).toContain 'c.coffee'
If you need to wait for multiple promises use a new
waitsForPromise function for each promise. (Caution: Without
beforeEach this example will fail!)
describe "waiting for the packages to load", -> beforeEach -> waitsForPromise -> atom.workspace.open('sample.js') waitsForPromise -> atom.packages.activatePackage('tabs') waitsForPromise -> atom.packages.activatePackage('tree-view') it 'should have waited long enough', -> expect(atom.packages.isPackageActive('tabs')).toBe true expect(atom.packages.isPackageActive('tree-view')).toBe true
Specs for asynchronous functions can be done using the
runs functions. A simple example.
describe "fs.readdir(path, cb)", -> it "is async", -> spy = jasmine.createSpy('fs.readdirSpy') fs.readdir('/tmp/example', spy) waitsFor -> spy.callCount > 0 runs -> exp = [null, ['example.coffee']] expect(spy.mostRecentCall.args).toEqual exp expect(spy).toHaveBeenCalledWith(null, ['example.coffee'])
For a more detailed documentation on asynchronous tests please visit the Jasmine documentation.
Most of the time you'll want to run specs by triggering the
window:run-package-specs command. This command is not only to run package specs, it can also be used to run Atom core specs when working on Atom itself. This will run all the specs in the current project's
To run a limited subset of specs use the
fit methods. You can use those to focus a single spec or several specs. Modified from the example above, focusing an individual spec looks like this:
describe "when a test is written", -> fit "has some expectations that should pass", -> expect("apples").toEqual("apples") expect("oranges").not.toEqual("apples")
To run tests on the command line, run Atom with the
--test flag followed by one or more paths to test files or directories. You can also specify a
--timeout option, which will force-terminate your tests after a certain number of seconds have passed.
atom --test --timeout 60 ./test/test-1.js ./test/test-2.js
Warning: This API is available as of 1.2.0-beta0, and it is experimental and subject to change. Test runner authors should be prepared to test their code against future beta releases until it stabilizes.
By default, package tests are run with Jasmine 1.3, which is outdated but can't be changed for compatibility reasons. You can specify your own custom test runner by including an
atomTestRunner field in your
package.json. Atom will require whatever module you specify in this field, so you can use a relative path or the name of a module in your package's dependencies.
Your test runner module must export a single function, which Atom will call within a new window to run your package's tests. Your function will be called with the following parameters:
testPathsAn array of paths to tests to run. Could be paths to files or directories.
buildAtomEnvironmentA function that can be called to construct an instance of the
atomglobal will be explicitly assigned, but you can assign one in your runner if desired. This function should be called with the following parameters:
applicationDelegateAn object responsible for Atom's interaction with the browser process and host OS. Use
buildDefaultApplicationDelegatefor a default instance. You can override specific methods on this object to prevent or test these interactions.
windowA window global.
documentA document global.
configDirPathA path to the configuration directory (usually
enablePersistenceA boolean indicating whether the Atom environment should save or load state from the file system. You probably want this to be
buildDefaultApplicationDelegateA function that builds a default instance of the application delegate, suitable to be passed as the
logFileAn optional path to a log file to which test output should be logged.
headlessA boolean indicating whether or not the tests are being run from the command line via
legacyTestRunnerThis function can be invoked to run the legacy Jasmine runner, giving your package a chance to transition to a new test runner while maintaining a subset of its tests in the old environment.
Your function should return a promise that resolves to an exit code when your tests are finish running. This exit code will be returned when running your tests via the command line.