Mithril 1.1.7

Testing

Mithril comes with a testing framework called ospec. What makes it different from most test frameworks is that it avoids all configurability for the sake of avoiding yak shaving and analysis paralysis.

The easist way to setup the test runner is to create an NPM script for it. Open your project's package.json file and edit the test line under the scripts section:

{
    "name": "my-project",
    "scripts": {
        "test": "ospec"
    }
}

Remember this is a JSON file, so object key names such as "test" must be inside of double quotes.

To setup a test suite, create a tests folder and inside of it, create a test file:

// file: tests/math-test.js
var o = require("mithril/ospec/ospec")

o.spec("math", function() {
    o("addition works", function() {
        o(1 + 2).equals(3)
    })
})

To run the test, use the command npm test. Ospec considers any Javascript file inside of a tests folder (anywhere in the project) to be a test.

npm test

Running mithril in a non-browser environment

Mithril has a few dependencies on globals that exist in all its supported browser environments but are missing in all non-browser environments. To work around this you can use the browser mocks that ship with the mithril npm package.

The simplest way to do this is ensure the following snippet of code runs before you include mithril itself in your project.

// Polyfill DOM env for mithril
global.window = require("mithril/test-utils/browserMock.js")();
global.document = window.document;

Once that snippet has been run you can require("mithril") and it should be quite happy.


Good testing practices

Generally speaking, there are two ways to write tests: upfront and after the fact.

Writing tests upfront requires specifications to be frozen. Upfront tests are a great way of codifying the rules that a yet-to-be-implemented API must obey. However, writing tests upfront may not be a suitable strategy if you don't have a reasonable idea of what your project will look like, if the scope of the API is not well known or if it's likely to change (e.g. based on previous history at the company).

Writing tests after the fact is a way to document the behavior of a system and avoid regressions. They are useful to ensure that obscure corner cases are not inadvertently broken and that previously fixed bugs do not get re-introduced by unrelated changes.


Unit testing

Unit testing is the practice of isolating a part of an application (typically a single module), and asserting that, given some inputs, it produces the expected outputs.

Testing a Mithril component is easy. Let's assume we have a simple component like this:

// MyComponent.js
var m = require("mithril")

module.exports = {
    view: function() {
        return m("div", 
            m("p", "Hello World")
        )
    }
}

We can then create a tests/MyComponent.js file and create a test for this component like this:

var MyComponent = require("MyComponent")

o.spec("MyComponent", function() {
    o("returns a div", function() {
        var vnode = MyComponent.view()

        o(vnode.tag).equals("div")
        o(vnode.children.length).equals(1)
        o(vnode.children[0].tag).equals("p")
        o(vnode.children[0].children).equals("Hello world")
    })
})

Typically, you wouldn't test the structure of the vnode tree so granularly, and you would instead only test non-trivial, dynamic aspects of the view. A tool that can help making testing easier with deep vnode trees is Mithril Query.

Sometimes, you need to mock the dependencies of a module in order to test the module in isolation. Mockery is one tool that allows you to do that.


License: MIT. © Leo Horie.