Ember CLI Blueprint Testing

@trabus

@jakebixby

Jake Bixby

Senior Engineer @

father

ember-cli core team member

corgi food balancing enthusiast

I may have a problem

He doesn't mind,  I promise.

Why?

What is the point of testing blueprints?

Testing your blueprints will save you a lot of pain if you're an addon developer.

It will alert you to ember-cli changes that may inadvertently break your blueprints.

It's a great way to test all permutations of your complicated blueprints.

How the heck do I test a blueprint in my addon?

  • generate the blueprint
  • ???
  • what even is happening

What are we actually testing here?

  • Can the blueprint be generated?
  • Can the blueprint be destroyed?
  • Are the generated contents correct?
  • Does it do any other expected work?
  • Test in node, since that's ember-cli's domain.
  • Only concerned with the blueprint, not generated content's functionality.

The 3 A's

  • Arrange
  • Act
  • Assert

x2!

Arrange

1. Generate a clean ember-cli project or addon

2. Step into the project or addon

3. Symlink your addon so the blueprint can be found

Act

1. Generate your blueprint with options

2. Optionally generate additional related blueprints

Assert

1. Assert blueprint ran successfully or threw an error (if expected)

2. Assert the content of the generated files, ui output, and changed files

Arrange

1. Already did that in the generate step!

2. No reason to leave a perfectly setup project laying around

Act

1. Destroy blueprint, generally using the same options as when it was generated

Assert

1. Assert files are gone and that destroy blueprint ran successfully or threw an error (if expected)

2. Assert the generated files, ui output, and changed files

Done.

(cleanup for the next test)

Some Background

it was all for the addons

The addonification of ember and ember-data prompted the extraction of blueprints, which included their tests.

We needed a way to quickly setup a temp project or addon, then generate and destroy from its context

(what's this all about then?)

ember-cli has had a form of blueprint test helpers from the beginning, but they weren't usable outside ember-cli.

Extraction happened

Many thanks to Tobias Bieniek (@turbo87), who helped bring this project to completion!

We've developed a simple set of promise based test helpers to allow you to setup, generate, destroy, and assert in many flexible permutations.

To support testing the extracted blueprints, we also extracted much of the test helpers and setup utilities.

We pass the testings on to you!

ember-cli-blueprint-test-helpers

https://github.com/ember-cli/ember-cli-blueprint-test-helpers

A library of test

helpers to:

  • Generate a clean test ember app/addon

  • Generate/Destroy blueprints in that test app/addon

  • Assert file contents with chai helpers

  • Assert cli output and errors

  • Manipulate your test's package.json

Fine Print

  • Addons only. Doesn't work for blueprints contained inside projects (but may work in the future)

  • Doesn't support generating inside engines (yet)

  • Generates tests with ES6 syntax, so Node 5.x or higher is recommended, or use babel

How to use them

ember install ember-cli-blueprint-test-helpers
ember g blueprint-test my-blueprint

This will generate a blueprint-test inside your addon's   node-tests folder. From there you can fill in your tests.

If you need babel

ember g ember-cli-blueprint-test-helpers --babel

This will install babel testing dependencies in your addon, including a .babelrc, and add an npm script which will run your tests with babel-register

Using the helpers

describe('Acceptance: ember generate and destroy my-blueprint', function() {
  // create and destroy temporary working directories
  setupTestHooks(this);

  it('my-blueprint foo', function() {
    var args = ['my-blueprint', 'foo'];

    // create a new Ember.js app in the working directory
    return emberNew()

      // then generate and destroy the `my-blueprint` blueprint called `foo`
      .then(() => emberGenerateDestroy(args, (file) => {

        // and run some assertions in between
        expect(file('path/to/file.js'))
          .to.contain('file contents to match')
          .to.contain('more file contents\n');
      }));

     // magically done for you: 
     // assert that the generated files are destroyed again 
  });
});

Finer control use

describe('Acceptance: ember generate and destroy my-blueprint', function() {
  // create and destroy temporary working directories
  setupTestHooks(this);

  it('my-blueprint foo', function() {
    var args = ['my-blueprint', 'foo'];

    // create a new Ember.js app in the working directory
    return emberNew()

      // then generate the `my-blueprint` blueprint called `foo`
      .then(() => emberGenerate(args))

      // then assert that the files were generated correctly
      .then(() => expect(file('path/to/file.js'))
        .to.contain('file contents to match')
        .to.contain('more file contents\n'))

      // then destroy the `my-blueprint` blueprint called `foo`
      .then(() => emberDestroy(args))

      // then assert that the files were destroyed correctly
      .then(() => expect(file('path/to/file.js')).to.not.exist);
  });
});

Asserting throws

describe('Acceptance: ember generate and destroy my-blueprint', function() {
  // create and destroy temporary working directories
  setupTestHooks(this);

  it('my-blueprint foo', function() {
    var args = ['my-blueprint', 'foo'];

    // create a new Ember.js app in the working directory
    return emberNew()
      // we only need to generate, since we're expecting an error
      .then(() => expect(emberGenerate(args))
        // use chai-as-promised rejectedWith helper
        .to.be.rejectedWith(SilentError, /Foo is overused/));
  });
});

Asserting output

describe('Acceptance: ember generate and destroy my-blueprint', function() {
  // create and destroy temporary working directories
  setupTestHooks(this);

  it('my-blueprint foo', function() {
    var args = ['my-blueprint', 'foo'];

    // create a new Ember.js app in the working directory
    return emberNew()
      // then generate the `my-blueprint` blueprint called `foo`
      .then(() => emberGenerate(args))

      // then assert that the files were generated correctly
      .then((result) => => {
        let output = result.outputStream.join('');
        // test ui output because we can't test for actual npm install modifying package.json
        expect(output).to.contain('npm-package')
      });
  });
});

Running the tests

mocha node-tests --recursive

If you installed the babel dependencies, your npm node-test script has been updated to:

mocha node-tests --recursive --require babel-register

Then you can run your tests through an npm script!

npm run nodetest

Recap

  • Use ember-cli-blueprint-test-helpers to test your generated blueprint output

  • Catch issues caused by upstream changes to ember-cli and prepare for future blueprints changes

  • Addon authors: the tools are available now, please test your addon's blueprints!

Thanks!

@trabus

@jakebixby

Ember CLI Blueprint Testing

By Jacob Bixby

Ember CLI Blueprint Testing

Introducing `ember-cli-blueprint-test-helpers`, blueprint testing for the masses.

  • 915