Repository: ember-cli/ember-cli
Pull request #7961 (2018-08-06 13:42:29)

Turn experiments object into a function
When testing an experiment in `ember-cli` scope, a new CI scenario is the go-to solution. This involves scopping tests like this:
```js
// in ember-cli
if (experiments.MY_EXPERIMENT) {
  describe('my experiment module', ...)
  // or
  module('my experiment module', ...)
} else {
  describe('regular module', ...)
  // or
  module('regular module', ...)
}
```

But when testing an experiment outside of `ember-cli` scope, things get different. And we may want to be able to test in sequence:

```js
// in ember-data or in another addon
describe('my addon blueprint', {
  it('works - in a classic app', ...);

  it('works - in a MU app', ...);
})
```

The issue is: as an object, [experiments](https://github.com/ember-cli/ember-cli/blob/v3.4.0-beta.2/lib/experiments/index.js#L25) is cached between the two tests (run on the same process). In these scenarios, we need not to cache the object, hence the function.

dcyriller: A use of this PR would be here https://github.com/ember-cli/ember-cli-blueprint-test-helpers/pull/153
rwjblue: I am not opposed to changing this, but I would prefer a slightly different API.

Seems like we can make this work:

```
const { isExperimentEnabled } = require('./lib/experiments');

if (isExperimentEnabled('MODULE_UNIFICATION')) {

}
```

What do you think?
dcyriller: It sounds more consistent indeed. I just updated the PR.
dcyriller: I manually relaunched the tests. One test suite is still failing, it's the one that generates coverage. But it's also failing on master.

Patch:
diff --git a/docs/experiments.md b/docs/experiments.md
index 2ec892792..05e3cf0fe 100644
--- a/docs/experiments.md
+++ b/docs/experiments.md
@@ -5,20 +5,19 @@
 They are defined in `lib/experiments/index.js`. For example:
 
 ```javascript
-let experiments = {
-  CONFIG_CACHING: isExperimentEnabled('CONFIG_CACHING'),
-};
-
+const availableExperiments = [
+  'CONFIG_CACHING',
+];
 ```
 
 When a new feature is added, all supporting code and tests **must** be guarded
 to ensure all tests pass when the feature is enabled _or_ disabled:
 
 ```javascript
-const experiments = require('../experiments');
+const { isExperimentEnabled } = require('../experiments');
 
 // ...snip...
-if (experiments.MODULE_UNIFICATION) {
+if (isExperimentEnabled('MODULE_UNIFICATION')) {
   ...
 } else {
   ...
diff --git a/lib/broccoli/ember-app.js b/lib/broccoli/ember-app.js
index 80743560e..e83b1a29c 100644
--- a/lib/broccoli/ember-app.js
+++ b/lib/broccoli/ember-app.js
@@ -37,7 +37,7 @@ const emberAppUtils = require('../utilities/ember-app-utils');
 const addonProcessTree = require('../utilities/addon-process-tree');
 const lintAddonsByType = require('../utilities/lint-addons-by-type');
 const emberCLIBabelConfigKey = require('../utilities/ember-cli-babel-config-key');
-const experiments = require('../experiments');
+const { isExperimentEnabled } = require('../experiments');
 const semver = require('semver');
 const DefaultPackager = require('./default-packager');
 
@@ -166,7 +166,7 @@ class EmberApp {
       additionalAssetPaths: this.otherAssetPaths,
       vendorTestStaticStyles: this.vendorTestStaticStyles,
       legacyTestFilesToAppend: this.legacyTestFilesToAppend,
-      isModuleUnificationEnabled: experiments.MODULE_UNIFICATION && !!this.trees.src,
+      isModuleUnificationEnabled: isExperimentEnabled('MODULE_UNIFICATION') && !!this.trees.src,
       distPaths: {
         appJsFile: this.options.outputPaths.app.js,
         appCssFile: this.options.outputPaths.app.css,
@@ -261,7 +261,7 @@ class EmberApp {
     // these are contained within app/ no need to watch again
     // (we should probably have the builder or the watcher dedup though)
 
-    if (experiments.MODULE_UNIFICATION) {
+    if (isExperimentEnabled('MODULE_UNIFICATION')) {
       let srcStylesPath = `${resolvePathFor('src', trees.src)}/ui/styles`;
       this._stylesPath = fs.existsSync(srcStylesPath) ? srcStylesPath : resolvePathFor('app/styles', trees.styles);
     } else {
@@ -965,7 +965,7 @@ class EmberApp {
       addons: this.project.addons,
       autoRun: this.options.autoRun,
       storeConfigInMeta: this.options.storeConfigInMeta,
-      isModuleUnification: experiments.MODULE_UNIFICATION && !!this.trees.src,
+      isModuleUnification: isExperimentEnabled('MODULE_UNIFICATION') && !!this.trees.src,
     });
 
     return new ConfigReplace(index, this._defaultPackager.packageConfig(this.tests), {
@@ -1026,7 +1026,7 @@ class EmberApp {
       annotation: 'ProcessedAppTree',
     });
 
-    if (experiments.PACKAGER && isPackageHookSupplied) {
+    if (isExperimentEnabled('PACKAGER') && isPackageHookSupplied) {
       appTree = this._precompileAppJsTree(appTree);
     }
 
@@ -1155,7 +1155,7 @@ class EmberApp {
       for (let addonBundle of addonBundles) {
         let { name, root } = addonBundle;
 
-        if (experiments.DELAYED_TRANSPILATION) {
+        if (isExperimentEnabled('DELAYED_TRANSPILATION')) {
           if (!options.moduleNormalizerDisabled) {
             // move legacy /modules/addon to /addon
             let hasAlreadyPrintedModuleDeprecation;
@@ -1386,7 +1386,7 @@ class EmberApp {
       lintTrees.push(lintedApp);
     }
 
-    if (experiments.MODULE_UNIFICATION && this.trees.src) {
+    if (isExperimentEnabled('MODULE_UNIFICATION') && this.trees.src) {
       let lintedSrc = this.addonLintTree('src', this.trees.src);
       lintedSrc = new Funnel(lintedSrc, {
         destDir: 'lint/src/',
@@ -1682,7 +1682,7 @@ class EmberApp {
   }
 
   _legacyPackage(fullTree) {
-    if (experiments.DELAYED_TRANSPILATION) {
+    if (isExperimentEnabled('DELAYED_TRANSPILATION')) {
       fullTree = mergeTrees(
         [
           fullTree,
@@ -1747,7 +1747,7 @@ class EmberApp {
 
     fullTree = this._debugTree(fullTree, 'prepackage');
 
-    if (experiments.PACKAGER) {
+    if (isExperimentEnabled('PACKAGER')) {
       if (this._isPackageHookSupplied) {
         packagedTree = packageFn.call(this, fullTree);
       } else {
diff --git a/lib/commands/new.js b/lib/commands/new.js
index 5f0eb0c98..f65224edd 100644
--- a/lib/commands/new.js
+++ b/lib/commands/new.js
@@ -9,7 +9,7 @@ const SilentError = require('silent-error');
 const validProjectName = require('../utilities/valid-project-name');
 const normalizeBlueprint = require('../utilities/normalize-blueprint-option');
 const mergeBlueprintOptions = require('../utilities/merge-blueprint-options');
-const experiments = require('../experiments');
+const { isExperimentEnabled } = require('../experiments');
 
 const rmdir = RSVP.denodeify(fs.remove);
 const Promise = RSVP.Promise;
@@ -38,7 +38,7 @@ module.exports = Command.extend({
   beforeRun: mergeBlueprintOptions,
 
   run(commandOptions, rawArgs) {
-    if (experiments.MODULE_UNIFICATION) {
+    if (isExperimentEnabled('MODULE_UNIFICATION')) {
       if (commandOptions.blueprint === 'app') {
         commandOptions.blueprint = 'module-unification-app';
       } else if (commandOptions.blueprint === 'addon') {
diff --git a/lib/experiments/index.js b/lib/experiments/index.js
index dbe5229e5..e74362ff9 100644
--- a/lib/experiments/index.js
+++ b/lib/experiments/index.js
@@ -2,8 +2,19 @@
 
 const CliError = require('../errors/cli');
 
-// eslint-disable-next-line no-unused
+const availableExperiments = [
+  'PACKAGER',
+  'MODULE_UNIFICATION',
+  'DELAYED_TRANSPILATION',
+  'BROCCOLI_2',
+  'SYSTEM_TEMP',
+];
+
 function isExperimentEnabled(experimentName) {
+  if (availableExperiments.indexOf(experimentName) < 0) {
+    return false;
+  }
+
   if (process.env.EMBER_CLI_ENABLE_ALL_EXPERIMENTS) {
     return true;
   }
@@ -21,14 +32,4 @@ if (isExperimentEnabled('SYSTEM_TEMP') && !isExperimentEnabled('BROCCOLI_2')) {
   throw new CliError('EMBER_CLI_SYSTEM_TEMP only works in combination with EMBER_CLI_BROCCOLI_2');
 }
 
-let experiments = {
-  PACKAGER: isExperimentEnabled('PACKAGER'),
-  MODULE_UNIFICATION: isExperimentEnabled('MODULE_UNIFICATION'),
-  DELAYED_TRANSPILATION: isExperimentEnabled('DELAYED_TRANSPILATION'),
-  BROCCOLI_2: isExperimentEnabled('BROCCOLI_2'),
-  SYSTEM_TEMP: isExperimentEnabled('BROCCOLI_2') && isExperimentEnabled('SYSTEM_TEMP'),
-};
-
-Object.freeze(experiments);
-
-module.exports = experiments;
+module.exports = { isExperimentEnabled };
diff --git a/lib/models/addon.js b/lib/models/addon.js
index d0154a70a..aea001d8c 100644
--- a/lib/models/addon.js
+++ b/lib/models/addon.js
@@ -11,7 +11,7 @@ const heimdallLogger = require('heimdalljs-logger');
 const logger = heimdallLogger('ember-cli:addon');
 const treeCacheLogger = heimdallLogger('ember-cli:addon:tree-cache');
 const cacheKeyLogger = heimdallLogger('ember-cli:addon:cache-key-for-tree');
-const experiments = require('../experiments');
+const { isExperimentEnabled } = require('../experiments');
 const PackageInfoCache = require('../models/package-info-cache');
 
 const p = require('ember-cli-preprocess-registry/preprocessors');
@@ -242,7 +242,7 @@ let addonProto = {
 
     this._initDefaultBabelOptions();
 
-    if (experiments.DELAYED_TRANSPILATION) {
+    if (isExperimentEnabled('DELAYED_TRANSPILATION')) {
       this.registry.remove('template', 'ember-cli-htmlbars');
     }
   },
@@ -253,7 +253,7 @@ let addonProto = {
     });
 
     let defaultEmberCLIBabelOptions;
-    if (experiments.DELAYED_TRANSPILATION) {
+    if (isExperimentEnabled('DELAYED_TRANSPILATION')) {
       defaultEmberCLIBabelOptions = {
         compileModules: false,
         disablePresetEnv: true,
@@ -746,7 +746,7 @@ let addonProto = {
     @return {Tree} App file tree
   */
   treeForApp(tree) {
-    if (!experiments.MODULE_UNIFICATION) {
+    if (!isExperimentEnabled('MODULE_UNIFICATION')) {
       return tree;
     } else if (this.project.isModuleUnification() && this.isModuleUnification()) {
       return null;
@@ -787,7 +787,7 @@ let addonProto = {
     @return
   */
   treeForSrc(rawSrcTree) {
-    if (!experiments.MODULE_UNIFICATION) {
+    if (!isExperimentEnabled('MODULE_UNIFICATION')) {
       return null;
     }
     if (!rawSrcTree) { return null; }
@@ -800,7 +800,7 @@ let addonProto = {
     let srcAfterPreprocessTreeHook = this._addonPreprocessTree('src', srcNamespacedTree);
 
     let srcAfterTemplatePreprocessing = srcAfterPreprocessTreeHook;
-    if (!experiments.DELAYED_TRANSPILATION || registryHasPreprocessor(this.registry, 'template')) {
+    if (!isExperimentEnabled('DELAYED_TRANSPILATION') || registryHasPreprocessor(this.registry, 'template')) {
       srcAfterTemplatePreprocessing = preprocessTemplates(srcAfterPreprocessTreeHook, {
         registry: this.registry,
         annotation: `Addon#treeForSrc(${this.name})`,
@@ -1131,7 +1131,7 @@ let addonProto = {
     this._requireBuildPackages();
 
     if (this.shouldCompileTemplates()) {
-      if (!experiments.DELAYED_TRANSPILATION && !registryHasPreprocessor(this.registry, 'template')) {
+      if (!isExperimentEnabled('DELAYED_TRANSPILATION') && !registryHasPreprocessor(this.registry, 'template')) {
         throw new SilentError(`Addon templates were detected, but there are no template compilers registered for \`${this.name}\`. ` +
           `Please make sure your template precompiler (commonly \`ember-cli-htmlbars\`) is listed in \`dependencies\` ` +
           `(NOT \`devDependencies\`) in \`${this.name}\`'s \`package.json\`.`);
@@ -1140,7 +1140,7 @@ let addonProto = {
       let preprocessedTemplateTree = this._addonPreprocessTree('template', this._addonTemplateFiles(addonTree));
 
       let processedTemplateTree;
-      if (!experiments.DELAYED_TRANSPILATION || registryHasPreprocessor(this.registry, 'template')) {
+      if (!isExperimentEnabled('DELAYED_TRANSPILATION') || registryHasPreprocessor(this.registry, 'template')) {
         processedTemplateTree = preprocessTemplates(preprocessedTemplateTree, {
           annotation: `compileTemplates(${this.name})`,
           registry: this.registry,
diff --git a/lib/models/builder.js b/lib/models/builder.js
index d3be153a7..2d7795769 100644
--- a/lib/models/builder.js
+++ b/lib/models/builder.js
@@ -11,7 +11,7 @@ const findBuildFile = require('../utilities/find-build-file');
 const _resetTreeCache = require('./addon')._resetTreeCache;
 const Sync = require('tree-sync');
 const heimdall = require('heimdalljs');
-const experiments = require('../experiments/index');
+const { isExperimentEnabled } = require('../experiments');
 
 /**
  * Wrapper for the Broccoli [Builder](https://github.com/broccolijs/broccoli/blob/master/lib/builder.js) class.
@@ -27,8 +27,8 @@ class Builder extends CoreObject {
   constructor(options) {
     super(options);
 
-    this.broccoli2 = experiments.BROCCOLI_2;
-    this.systemTemp = experiments.SYSTEM_TEMP;
+    this.broccoli2 = isExperimentEnabled('BROCCOLI_2');
+    this.systemTemp = isExperimentEnabled('SYSTEM_TEMP');
 
     this.setupBroccoliBuilder();
 
diff --git a/lib/models/project.js b/lib/models/project.js
index dce12e455..2fd451ddf 100644
--- a/lib/models/project.js
+++ b/lib/models/project.js
@@ -215,8 +215,8 @@ class Project {
      @return {Boolean} Whether this is using a module unification format.
     */
   isModuleUnification() {
-    const experiments = require('../experiments');
-    if (experiments.MODULE_UNIFICATION) {
+    const { isExperimentEnabled } = require('../experiments');
+    if (isExperimentEnabled('MODULE_UNIFICATION')) {
       return this.has('src');
     } else {
       return false;


Test patch:
diff --git a/tests/acceptance/addon-generate-test.js b/tests/acceptance/addon-generate-test.js
index cf711c56a..12027010f 100644
--- a/tests/acceptance/addon-generate-test.js
+++ b/tests/acceptance/addon-generate-test.js
@@ -13,7 +13,7 @@ let tmproot = path.join(root, 'tmp');
 const Blueprint = require('../../lib/models/blueprint');
 const BlueprintNpmTask = require('ember-cli-internal-test-helpers/lib/helpers/disable-npm-on-blueprint');
 const mkTmpDirIn = require('../../lib/utilities/mk-tmp-dir-in');
-const experiments = require('../../lib/experiments');
+const { isExperimentEnabled } = require('../../lib/experiments');
 
 const chai = require('../chai');
 let expect = chai.expect;
@@ -91,7 +91,7 @@ describe('Acceptance: ember generate in-addon', function() {
     expect(file('app/services/session.js')).to.exist;
   }));
 
-  if (experiments.MODULE_UNIFICATION) {
+  if (isExperimentEnabled('MODULE_UNIFICATION')) {
     it('does not run the `addon-import` blueprint from a module unification addon', co.wrap(function *() {
       yield initAddon('my-addon');
       yield ensureDir('src');
@@ -127,7 +127,7 @@ describe('Acceptance: ember generate in-addon', function() {
     expect(file('app/services/session.js')).to.exist;
   }));
 
-  if (experiments.MODULE_UNIFICATION) {
+  if (isExperimentEnabled('MODULE_UNIFICATION')) {
     it('skips a custom "*-addon" blueprint from a module unification addon', co.wrap(function *() {
       yield initAddon('my-addon');
       yield ensureDir('src');
diff --git a/tests/acceptance/addon-mu-smoke-test-slow.js b/tests/acceptance/addon-mu-smoke-test-slow.js
index c2da42a19..6dde27479 100644
--- a/tests/acceptance/addon-mu-smoke-test-slow.js
+++ b/tests/acceptance/addon-mu-smoke-test-slow.js
@@ -4,7 +4,7 @@ const co = require('co');
 const path = require('path');
 const fs = require('fs-extra');
 
-const experiments = require('../../lib/experiments');
+const { isExperimentEnabled } = require('../../lib/experiments');
 const runCommand = require('../helpers/run-command');
 const copyFixtureFiles = require('../helpers/copy-fixture-files');
 const acceptance = require('../helpers/acceptance');
@@ -20,7 +20,7 @@ let dir = chai.dir;
 let addonName = 'some-cool-addon';
 let addonRoot;
 
-if (experiments.MODULE_UNIFICATION) {
+if (isExperimentEnabled('MODULE_UNIFICATION')) {
   describe.skip('Acceptance: addon-mu-smoke-test', function() {
     this.timeout(450000);
 
diff --git a/tests/acceptance/addon-smoke-test-slow.js b/tests/acceptance/addon-smoke-test-slow.js
index 12b6cd751..44e0169b2 100644
--- a/tests/acceptance/addon-smoke-test-slow.js
+++ b/tests/acceptance/addon-smoke-test-slow.js
@@ -7,7 +7,7 @@ const fs = require('fs-extra');
 const spawn = require('child_process').spawn;
 const chalk = require('chalk');
 
-const experiments = require('../../lib/experiments');
+const { isExperimentEnabled } = require('../../lib/experiments');
 const runCommand = require('../helpers/run-command');
 const ember = require('../helpers/ember');
 const copyFixtureFiles = require('../helpers/copy-fixture-files');
@@ -24,7 +24,7 @@ let dir = chai.dir;
 let addonName = 'some-cool-addon';
 let addonRoot;
 
-if (!experiments.MODULE_UNIFICATION) {
+if (!isExperimentEnabled('MODULE_UNIFICATION')) {
   describe('Acceptance: addon-smoke-test', function() {
     this.timeout(450000);
 
diff --git a/tests/acceptance/brocfile-smoke-test-slow.js b/tests/acceptance/brocfile-smoke-test-slow.js
index 73be60ddb..c9bd70ef2 100644
--- a/tests/acceptance/brocfile-smoke-test-slow.js
+++ b/tests/acceptance/brocfile-smoke-test-slow.js
@@ -6,7 +6,7 @@ const path = require('path');
 const fs = require('fs-extra');
 let remove = RSVP.denodeify(fs.remove);
 
-const experiments = require('../../lib/experiments');
+const { isExperimentEnabled } = require('../../lib/experiments');
 const runCommand = require('../helpers/run-command');
 const acceptance = require('../helpers/acceptance');
 const copyFixtureFiles = require('../helpers/copy-fixture-files');
@@ -53,7 +53,7 @@ describe('Acceptance: brocfile-smoke-test', function() {
     expect(vendorContents).to.contain(expected, 'EmberENV should be in assets/vendor.js');
   }));
 
-  if (!experiments.MODULE_UNIFICATION) {
+  if (!isExperimentEnabled('MODULE_UNIFICATION')) {
     it('a custom environment config can be used in Brocfile.js', co.wrap(function *() {
       yield copyFixtureFiles('brocfile-tests/custom-environment-config');
       yield runCommand(path.join('.', 'node_modules', 'ember-cli', 'bin', 'ember'), 'test');
@@ -87,7 +87,7 @@ describe('Acceptance: brocfile-smoke-test', function() {
     }
   }));
 
-  if (!experiments.MODULE_UNIFICATION) {
+  if (!isExperimentEnabled('MODULE_UNIFICATION')) {
     it('using autoRun: true', co.wrap(function *() {
       yield copyFixtureFiles('brocfile-tests/auto-run-true');
       yield runCommand(path.join('.', 'node_modules', 'ember-cli', 'bin', 'ember'), 'build');
@@ -162,7 +162,7 @@ describe('Acceptance: brocfile-smoke-test', function() {
     expect(subjectFileContents).to.equal('ROOT FILE\n');
   }));
 
-  if (!experiments.MODULE_UNIFICATION) {
+  if (!isExperimentEnabled('MODULE_UNIFICATION')) {
     it('using pods based templates', co.wrap(function *() {
       yield copyFixtureFiles('brocfile-tests/pods-templates');
       yield runCommand(path.join('.', 'node_modules', 'ember-cli', 'bin', 'ember'), 'test');
@@ -358,7 +358,7 @@ describe('Acceptance: brocfile-smoke-test', function() {
     expect(file(path.join(basePath, '/assets/some-cool-app.css'))).to.not.exist;
   }));
 
-  if (!experiments.MODULE_UNIFICATION) {
+  if (!isExperimentEnabled('MODULE_UNIFICATION')) {
     it('multiple paths can be CSS preprocessed', co.wrap(function *() {
       yield copyFixtureFiles('brocfile-tests/multiple-sass-files');
 
@@ -382,7 +382,7 @@ describe('Acceptance: brocfile-smoke-test', function() {
     expect(file(`dist/assets/${appName}.css`)).to.exist;
   }));
 
-  if (!experiments.MODULE_UNIFICATION) {
+  if (!isExperimentEnabled('MODULE_UNIFICATION')) {
     // for backwards compat.
     it('app.scss is output to <app name>.css by default', co.wrap(function *() {
       yield copyFixtureFiles('brocfile-tests/multiple-sass-files');
diff --git a/tests/acceptance/new-test.js b/tests/acceptance/new-test.js
index e9779db89..17e3d900b 100644
--- a/tests/acceptance/new-test.js
+++ b/tests/acceptance/new-test.js
@@ -18,7 +18,7 @@ let file = chai.file;
 let dir = chai.dir;
 const forEach = require('ember-cli-lodash-subset').forEach;
 const assertVersionLock = require('../helpers/assert-version-lock');
-const experiments = require('../../lib/experiments');
+const { isExperimentEnabled } = require('../../lib/experiments');
 
 let tmpDir = './tmp/new-test';
 
@@ -52,13 +52,13 @@ describe('Acceptance: ember new', function() {
   }
 
   function confirmBlueprintedApp() {
-    if (experiments.MODULE_UNIFICATION) {
+    if (isExperimentEnabled('MODULE_UNIFICATION')) {
       return confirmBlueprintedForDir('blueprints/module-unification-app');
     }
     return confirmBlueprintedForDir('blueprints/app');
   }
 
-  if (experiments.MODULE_UNIFICATION) {
+  if (isExperimentEnabled('MODULE_UNIFICATION')) {
     it('EMBER_CLI_MODULE_UNIFICATION: ember addon foo works', co.wrap(function *() {
       yield ember([
         'addon',
@@ -453,7 +453,7 @@ describe('Acceptance: ember new', function() {
       checkPackageJson(fixturePath);
     }));
 
-    if (!experiments.MODULE_UNIFICATION) {
+    if (!isExperimentEnabled('MODULE_UNIFICATION')) {
       it('app + npm + !welcome', co.wrap(function *() {
         yield ember([
           'new',
diff --git a/tests/acceptance/preprocessor-smoke-test-slow.js b/tests/acceptance/preprocessor-smoke-test-slow.js
index 5de7158d6..6176c4072 100644
--- a/tests/acceptance/preprocessor-smoke-test-slow.js
+++ b/tests/acceptance/preprocessor-smoke-test-slow.js
@@ -4,7 +4,7 @@ const co = require('co');
 const path = require('path');
 const fs = require('fs-extra');
 
-const experiments = require('../../lib/experiments');
+const { isExperimentEnabled } = require('../../lib/experiments');
 const runCommand = require('../helpers/run-command');
 const acceptance = require('../helpers/acceptance');
 const copyFixtureFiles = require('../helpers/copy-fixture-files');
@@ -21,7 +21,7 @@ let dir = chai.dir;
 let appName = 'some-cool-app';
 let appRoot;
 
-if (!experiments.MODULE_UNIFICATION) {
+if (!isExperimentEnabled('MODULE_UNIFICATION')) {
   describe('Acceptance: preprocessor-smoke-test', function() {
     this.timeout(360000);
 
diff --git a/tests/acceptance/smoke-test-slow.js b/tests/acceptance/smoke-test-slow.js
index 41c7cead7..dc0ad57b0 100644
--- a/tests/acceptance/smoke-test-slow.js
+++ b/tests/acceptance/smoke-test-slow.js
@@ -7,7 +7,7 @@ const crypto = require('crypto');
 const walkSync = require('walk-sync');
 const EOL = require('os').EOL;
 
-const experiments = require('../../lib/experiments');
+const { isExperimentEnabled } = require('../../lib/experiments');
 const runCommand = require('../helpers/run-command');
 const acceptance = require('../helpers/acceptance');
 const copyFixtureFiles = require('../helpers/copy-fixture-files');
@@ -48,7 +48,7 @@ describe('Acceptance: smoke-test', function() {
     return runCommand(path.join('.', 'node_modules', 'ember-cli', 'bin', 'ember'), 'test');
   });
 
-  if (!experiments.MODULE_UNIFICATION) {
+  if (!isExperimentEnabled('MODULE_UNIFICATION')) {
     it('ember new foo, make sure addon template overwrites', co.wrap(function *() {
       yield ember(['generate', 'template', 'foo']);
       yield ember(['generate', 'in-repo-addon', 'my-addon']);
@@ -195,7 +195,7 @@ describe('Acceptance: smoke-test', function() {
     expect(paths).to.have.length.below(24, `expected fewer than 24 files in dist, found ${paths.length}`);
   }));
 
-  if (!experiments.MODULE_UNIFICATION) {
+  if (!isExperimentEnabled('MODULE_UNIFICATION')) {
     it('ember build exits with non-zero code when build fails', co.wrap(function *() {
       let appJsPath = path.join(appRoot, 'app', 'app.js');
       let ouputContainsBuildFailed = false;
@@ -249,7 +249,7 @@ describe('Acceptance: smoke-test', function() {
     });
   }));
 
-  if (!experiments.MODULE_UNIFICATION) {
+  if (!isExperimentEnabled('MODULE_UNIFICATION')) {
     it('ember new foo, build --watch development, and verify rebuilt after change', co.wrap(function *() {
       let touched = false;
       let appJsPath = path.join(appRoot, 'app', 'app.js');
@@ -399,7 +399,7 @@ describe('Acceptance: smoke-test', function() {
     });
   }));
 
-  if (!experiments.MODULE_UNIFICATION) {
+  if (!isExperimentEnabled('MODULE_UNIFICATION')) {
     it('ember can override and reuse the built-in blueprints', co.wrap(function *() {
       yield copyFixtureFiles('addon/with-blueprint-override');
 
diff --git a/tests/unit/blueprints/in-repo-addon-test.js b/tests/unit/blueprints/in-repo-addon-test.js
index 55c0b3126..e9a8529ae 100644
--- a/tests/unit/blueprints/in-repo-addon-test.js
+++ b/tests/unit/blueprints/in-repo-addon-test.js
@@ -11,14 +11,14 @@ const td = require('testdouble');
 
 const expect = require('ember-cli-blueprint-test-helpers/chai').expect;
 const file = require('ember-cli-blueprint-test-helpers/chai').file;
-const experiments = require('../../../lib/experiments');
+const { isExperimentEnabled } = require('../../../lib/experiments');
 
 describe('Acceptance: ember generate and destroy in-repo-addon', function() {
   setupTestHooks(this, {
     cliPath: path.resolve(`${__dirname}/../../..`),
   });
 
-  if (experiments.MODULE_UNIFICATION) {
+  if (isExperimentEnabled('MODULE_UNIFICATION')) {
     describe('module unification app', function() {
       it('in-repo-addon fooBar', function() {
         let args = ['in-repo-addon', 'fooBar'];
diff --git a/tests/unit/broccoli/default-packager/index-test.js b/tests/unit/broccoli/default-packager/index-test.js
index 75d42667e..326c4e7de 100644
--- a/tests/unit/broccoli/default-packager/index-test.js
+++ b/tests/unit/broccoli/default-packager/index-test.js
@@ -4,7 +4,7 @@ const co = require('co');
 const expect = require('chai').expect;
 const DefaultPackager = require('../../../../lib/broccoli/default-packager');
 const broccoliTestHelper = require('broccoli-test-helper');
-const experiments = require('../../../../lib/experiments');
+const { isExperimentEnabled } = require('../../../../lib/experiments');
 
 const buildOutput = broccoliTestHelper.buildOutput;
 const createTempDir = broccoliTestHelper.createTempDir;
@@ -138,7 +138,7 @@ describe('Default Packager: Index', function() {
     expect(indexContent).to.equal(META_TAG);
   }));
 
-  if (experiments.MODULE_UNIFICATION) {
+  if (isExperimentEnabled('MODULE_UNIFICATION')) {
     describe('with module unification', function() {
       let input, output;
 
diff --git a/tests/unit/broccoli/default-packager/javascript-test.js b/tests/unit/broccoli/default-packager/javascript-test.js
index 63760347f..d49d4a182 100644
--- a/tests/unit/broccoli/default-packager/javascript-test.js
+++ b/tests/unit/broccoli/default-packager/javascript-test.js
@@ -6,7 +6,7 @@ const Funnel = require('broccoli-funnel');
 const DefaultPackager = require('../../../../lib/broccoli/default-packager');
 const broccoliTestHelper = require('broccoli-test-helper');
 const defaultPackagerHelpers = require('../../../helpers/default-packager');
-const experiments = require('../../../../lib/experiments');
+const { isExperimentEnabled } = require('../../../../lib/experiments');
 
 const buildOutput = broccoliTestHelper.buildOutput;
 const createTempDir = broccoliTestHelper.createTempDir;
@@ -299,7 +299,7 @@ describe('Default Packager: Javascript', function() {
 
 });
 
-if (experiments.MODULE_UNIFICATION) {
+if (isExperimentEnabled('MODULE_UNIFICATION')) {
   // there is a little code duplication here (mainly the ceremony around
   // setting up the folder structure and disposing of it after the tests are
   // executed; once we enable MU flag by defaul, we should clean this up a tad
diff --git a/tests/unit/broccoli/default-packager/process-test.js b/tests/unit/broccoli/default-packager/process-test.js
index 0cacf24ab..e81f2200b 100644
--- a/tests/unit/broccoli/default-packager/process-test.js
+++ b/tests/unit/broccoli/default-packager/process-test.js
@@ -6,7 +6,7 @@ const Funnel = require('broccoli-funnel');
 const DefaultPackager = require('../../../../lib/broccoli/default-packager');
 const broccoliTestHelper = require('broccoli-test-helper');
 const defaultPackagerHelpers = require('../../../helpers/default-packager');
-const experiments = require('../../../../lib/experiments');
+const { isExperimentEnabled } = require('../../../../lib/experiments');
 
 const buildOutput = broccoliTestHelper.buildOutput;
 const createTempDir = broccoliTestHelper.createTempDir;
@@ -119,7 +119,7 @@ describe('Default Packager: Process Javascript', function() {
     expect(defaultPackager._cachedProcessedAppAndDependencies._annotation).to.equal('Processed Application and Dependencies');
   }));
 
-  if (experiments.MODULE_UNIFICATION) {
+  if (isExperimentEnabled('MODULE_UNIFICATION')) {
     it('merges src with with app', co.wrap(function *() {
       let input = yield createTempDir();
 
diff --git a/tests/unit/broccoli/default-packager/styles-test.js b/tests/unit/broccoli/default-packager/styles-test.js
index b7a773e7a..6e8a9db24 100644
--- a/tests/unit/broccoli/default-packager/styles-test.js
+++ b/tests/unit/broccoli/default-packager/styles-test.js
@@ -3,7 +3,7 @@
 const co = require('co');
 const expect = require('chai').expect;
 const Funnel = require('broccoli-funnel');
-const experiments = require('../../../../lib/experiments');
+const { isExperimentEnabled } = require('../../../../lib/experiments');
 const DefaultPackager = require('../../../../lib/broccoli/default-packager');
 const broccoliTestHelper = require('broccoli-test-helper');
 const defaultPackagerHelpers = require('../../../helpers/default-packager');
@@ -389,7 +389,7 @@ describe('Default Packager: Styles', function() {
     expect(outputFiles.assets['vendor.css']).to.equal('first\nsecond\nthird');
   }));
 
-  if (experiments.MODULE_UNIFICATION) {
+  if (isExperimentEnabled('MODULE_UNIFICATION')) {
     describe('with module unification layout', function() {
       let importFilesMap = {
         '/assets/vendor.css': [
diff --git a/tests/unit/broccoli/default-packager/tests-test.js b/tests/unit/broccoli/default-packager/tests-test.js
index c2f3782ea..27e3377f1 100644
--- a/tests/unit/broccoli/default-packager/tests-test.js
+++ b/tests/unit/broccoli/default-packager/tests-test.js
@@ -4,7 +4,7 @@ const co = require('co');
 const stew = require('broccoli-stew');
 const Funnel = require('broccoli-funnel');
 const expect = require('chai').expect;
-const experiments = require('../../../../lib/experiments');
+const { isExperimentEnabled } = require('../../../../lib/experiments');
 const DefaultPackager = require('../../../../lib/broccoli/default-packager');
 const broccoliTestHelper = require('broccoli-test-helper');
 const defaultPackagerHelpers = require('../../../helpers/default-packager');
@@ -511,7 +511,7 @@ describe('Default Packager: Tests', function() {
     expect(outputFiles.assets['test-support.css']).to.include('a.css\nb.css');
   }));
 
-  if (experiments.MODULE_UNIFICATION) {
+  if (isExperimentEnabled('MODULE_UNIFICATION')) {
     describe('with module unification layout', function() {
       let input, output;
 
diff --git a/tests/unit/broccoli/ember-app-test.js b/tests/unit/broccoli/ember-app-test.js
index 0d40b3313..d3506428d 100644
--- a/tests/unit/broccoli/ember-app-test.js
+++ b/tests/unit/broccoli/ember-app-test.js
@@ -13,7 +13,7 @@ const buildOutput = broccoliTestHelper.buildOutput;
 const createTempDir = broccoliTestHelper.createTempDir;
 
 const MockCLI = require('../../helpers/mock-cli');
-const experiments = require('../../../lib/experiments');
+const { isExperimentEnabled } = require('../../../lib/experiments');
 const mergeTrees = require('../../../lib/broccoli/merge-trees');
 
 let EmberApp = require('../../../lib/broccoli/ember-app');
@@ -55,7 +55,7 @@ describe('EmberApp', function() {
     project = setupProject(projectPath);
   });
 
-  if (experiments.PACKAGER) {
+  if (isExperimentEnabled('PACKAGER')) {
     describe('packager hook', function() {
       let js, input, output;
 
diff --git a/tests/unit/broccoli/ember-app/app-and-dependencies-test.js b/tests/unit/broccoli/ember-app/app-and-dependencies-test.js
index da6d809a4..58b149f4b 100644
--- a/tests/unit/broccoli/ember-app/app-and-dependencies-test.js
+++ b/tests/unit/broccoli/ember-app/app-and-dependencies-test.js
@@ -11,7 +11,7 @@ const Project = require('../../../../lib/models/project');
 const buildOutput = broccoliTestHelper.buildOutput;
 const createTempDir = broccoliTestHelper.createTempDir;
 const walkSync = require('walk-sync');
-const experiments = require("../../../../lib/experiments/index.js");
+const { isExperimentEnabled } = require('../../../../lib/experiments');
 
 describe('EmberApp#appAndDependencies', function() {
   let input, output;
@@ -134,7 +134,7 @@ describe('EmberApp#appAndDependencies', function() {
     );
   }));
 
-  if (experiments.DELAYED_TRANSPILATION) {
+  if (isExperimentEnabled('DELAYED_TRANSPILATION')) {
     it('amdFunnelDisabled', co.wrap(function *() {
       input.write({
         'node_modules': {
diff --git a/tests/unit/commands/addon-test.js b/tests/unit/commands/addon-test.js
index 7ff346e75..bb8b9317a 100644
--- a/tests/unit/commands/addon-test.js
+++ b/tests/unit/commands/addon-test.js
@@ -5,7 +5,7 @@ const commandOptions = require('../../factories/command-options');
 const map = require('ember-cli-lodash-subset').map;
 const AddonCommand = require('../../../lib/commands/addon');
 const Blueprint = require('../../../lib/models/blueprint');
-const experiments = require('../../../lib/experiments');
+const { isExperimentEnabled } = require('../../../lib/experiments');
 const td = require('testdouble');
 
 describe('addon command', function() {
@@ -72,7 +72,7 @@ describe('addon command', function() {
     });
   });
 
-  if (!experiments.MODULE_UNIFICATION) {
+  if (!isExperimentEnabled('MODULE_UNIFICATION')) {
     it('doesn\'t allow to create an addon when the name is a period', function() {
       return expect(command.validateAndRun(['.'])).to.be.rejected.then(error => {
         expect(error.message).to.equal('Trying to generate an addon structure in this directory? Use `ember init` instead.');
diff --git a/tests/unit/commands/new-test.js b/tests/unit/commands/new-test.js
index 2ff4f8a47..3c089e8f7 100644
--- a/tests/unit/commands/new-test.js
+++ b/tests/unit/commands/new-test.js
@@ -8,7 +8,7 @@ const Promise = require('rsvp').Promise;
 const Blueprint = require('../../../lib/models/blueprint');
 const Command = require('../../../lib/models/command');
 const Task = require('../../../lib/models/task');
-const experiments = require('../../../lib/experiments');
+const { isExperimentEnabled } = require('../../../lib/experiments');
 const td = require('testdouble');
 
 describe('new command', function() {
@@ -77,7 +77,7 @@ describe('new command', function() {
     });
   });
 
-  if (!experiments.MODULE_UNIFICATION) {
+  if (!isExperimentEnabled('MODULE_UNIFICATION')) {
     it('shows a suggestion messages when the application name is a period', function() {
       return expect(command.validateAndRun(['.'])).to.be.rejected.then(error => {
         expect(error.message).to.equal('Trying to generate an application structure in this directory? Use `ember init` instead.');
diff --git a/tests/unit/models/builder-test.js b/tests/unit/models/builder-test.js
index fb2ff16f5..0b7ffb5c8 100644
--- a/tests/unit/models/builder-test.js
+++ b/tests/unit/models/builder-test.js
@@ -9,7 +9,7 @@ const rimraf = require('rimraf');
 const fixturify = require('fixturify');
 const MockProject = require('../../helpers/mock-project');
 const mkTmpDirIn = require('../../../lib/utilities/mk-tmp-dir-in');
-const experiments = require('../../../lib/experiments/index');
+const { isExperimentEnabled } = require('../../../lib/experiments');
 const td = require('testdouble');
 const chai = require('../../chai');
 let expect = chai.expect;
@@ -209,7 +209,7 @@ describe('models/builder.js', function() {
       });
     });
 
-    if (!experiments.SYSTEM_TEMP) {
+    if (!isExperimentEnabled('SYSTEM_TEMP')) {
       it('writes temp files to project root by default', function() {
         const project = new MockProject();
         project.root += '/tests/fixtures/build/simple';
@@ -225,7 +225,7 @@ describe('models/builder.js', function() {
       });
     }
 
-    if (experiments.SYSTEM_TEMP) {
+    if (isExperimentEnabled('SYSTEM_TEMP')) {
       it('writes temp files to Broccoli temp dir when EMBER_CLI_SYSTEM_TEMP=1', function() {
         const project = new MockProject();
         project.root += '/tests/fixtures/build/simple';
@@ -268,7 +268,7 @@ describe('models/builder.js', function() {
 
       return builder.build().then(function(result) {
         expect(Object.keys(result)).to.eql(['directory', 'graph']);
-        if (experiments.BROCCOLI_2) {
+        if (isExperimentEnabled('BROCCOLI_2')) {
           expect(result.graph.__heimdall__).to.not.be.undefined;
         } else {
           expect(result.graph.constructor.name).to.equal('Node');
diff --git a/tests/unit/models/project-test.js b/tests/unit/models/project-test.js
index def260f0b..90213a8d3 100644
--- a/tests/unit/models/project-test.js
+++ b/tests/unit/models/project-test.js
@@ -10,7 +10,7 @@ const expect = require('chai').expect;
 const emberCLIVersion = require('../../../lib/utilities/version-utils').emberCLIVersion;
 const td = require('testdouble');
 const MockCLI = require('../../helpers/mock-cli');
-const experiments = require('../../../lib/experiments');
+const { isExperimentEnabled } = require('../../../lib/experiments');
 
 describe('models/project.js', function() {
   let project, projectPath, packageContents;
@@ -551,7 +551,7 @@ describe('models/project.js', function() {
     });
   });
 
-  if (experiments.MODULE_UNIFICATION) {
+  if (isExperimentEnabled('MODULE_UNIFICATION')) {
     describe('isModuleUnification', function() {
       beforeEach(function() {
         projectPath = `${process.cwd()}/tmp/test-app`;