ccampbell / luna-testing

Simple, modern, opinionated JavaScript unit testing
https://craig.is/testing/code
MIT License
151 stars 6 forks source link

Snapshot testing #4

Open puregarlic opened 6 years ago

puregarlic commented 6 years ago

Have you considered adding snapshot testing to Luna? Since it's becoming an increasingly more popular test method, I was wondering if there was a way to bring that functionality to this framework. I was looking into just implementing it myself in my own tests using snap-shot-core and some other libraries, but I was thinking it would be nice if the t test function parameter also had a snapshot method in addition to assert.

However, I felt like that would interrupt some of the simplicity of what you're aiming for with Luna. Your thoughts?

ccampbell commented 6 years ago

Hey. I am definitely not opposed to the idea. I am not really familiar with how this works in other testing frameworks. Is it a matter of taking screenshots and then saving them somewhere on disk to look at or do you provide an image of what it is expected to look like and then the framework asserts that the snapshot matches the expected image? What do you think it should look like?

puregarlic commented 6 years ago

Snapshot testing is accomplished by taking a component, rendering it into JSON object, and then saving it to disk. Then, when you run the test a second time, it'll render the component again and compare it to the copy saved to disk. This is an example snapshot file generated by Jest, which pretty-prints the component into a template literal for readability in PRs and such.

I really like the AVA API for snapshot testing, and I think it would work well for Luna:

import test from 'ava';
import React from 'react';
import render from 'react-test-renderer';
import HelloWorld from '.';

test('HelloWorld component', t => {
    const tree = render.create(<HelloWorld/>).toJSON();
    t.snapshot(tree);
});

Running the test the first time generates the snapshot file, and the running it again checks the JSONified component against the snapshot file.

puregarlic commented 6 years ago

So perhaps in Luna, an implementation would look something like this:

// components/HelloWorld.js
import render from 'react-test-renderer'

export function testHelloWorld (t) {
  const tree = render.create(<HelloWorld/>).toJSON()
  t.snapshot(tree)
}

Which generates the following snapshot file:

// snapshots/components/HelloWorld.snap.js
export const testHelloWorld = `
<h1>
  Hello World...!
</h1>
`

Or something along those lines.

ccampbell commented 6 years ago

Oh. So this looks like it is a “snapshot” of the actual render tree with the current state and properties. I thought you meant snapshots like actual screenshots of the browser rendering itself. It seems like what you are more describing is capturing the state of components at a given time.

I like the idea, but I’m not sure I want to introduce a lot of React specific code in the aim of keeping Luna simple. There might be a more generic way of doing it though.

For example, maybe the test can snapshot the generated HTML from the browser, store that, and compare it on subsequent renders. I’m not sure if that would achieve the same thing or not though.

puregarlic commented 6 years ago

That might work, but my concern would be capturing all the props passed to the component you wanted to test.

Perhaps we could just take a step back and say that t.snapshot takes a value, stringifies it, and then saves it to a snapshot file. Then, on the next run, it compares the passed value to the snapshot. That way, people could bring their own renderers, or even use it for other data values. React testers would have to bring the test renderer and optionally a formatter, but I don't think that's too much of an issue. I could write a package to make that quick and easy.