diff --git a/lib/config/migrate-validate.js b/lib/config/migrate-validate.js
new file mode 100644
index 0000000000000000000000000000000000000000..65fa3d2ce8ea86ab0791804469ec54c2cfe87145
--- /dev/null
+++ b/lib/config/migrate-validate.js
@@ -0,0 +1,33 @@
+const configMigration = require('./migration');
+const configMassage = require('./massage');
+const configValidation = require('./validation');
+
+module.exports = {
+  migrateAndValidate,
+};
+
+function migrateAndValidate(config, input) {
+  const { logger } = config;
+  const { isMigrated, migratedConfig } = configMigration.migrateConfig(input);
+  if (isMigrated) {
+    logger.info(
+      { oldConfig: input, newConfig: migratedConfig },
+      'Config migration necessary'
+    );
+  }
+  const massagedConfig = configMassage.massageConfig(migratedConfig);
+  const { warnings, errors } = configValidation.validateConfig(massagedConfig);
+  // istanbul ignore if
+  if (warnings.length) {
+    logger.debug({ warnings }, 'Found renovate config warnings');
+  }
+  if (errors.length) {
+    logger.warn({ errors }, 'Found renovate config errors');
+  }
+  if (!config.repoIsOnboarded) {
+    // TODO #556 - enable warnings in real PRs
+    massagedConfig.warnings = (config.warnings || []).concat(warnings);
+    massagedConfig.errors = (config.errors || []).concat(errors);
+  }
+  return massagedConfig;
+}
diff --git a/lib/workers/repository/apis.js b/lib/workers/repository/apis.js
index e1613883ca38d1dd14771449ad92ddbfdc50f89e..5c766522715741a66be4f982946120c75def2139 100644
--- a/lib/workers/repository/apis.js
+++ b/lib/workers/repository/apis.js
@@ -3,9 +3,6 @@ const conventionalCommitsDetector = require('conventional-commits-detector');
 const path = require('path');
 const jsonValidator = require('json-dup-key-validator');
 const configParser = require('../../config');
-const configMigration = require('../../config/migration');
-const configMassage = require('../../config/massage');
-const configValidation = require('../../config/validation');
 const presets = require('../../config/presets');
 // API
 const githubPlatform = require('../../platform/github');
@@ -13,6 +10,7 @@ const gitlabPlatform = require('../../platform/gitlab');
 const dockerResolve = require('../../manager/docker/resolve');
 
 const { decryptConfig } = require('../../config/decrypt');
+const { migrateAndValidate } = require('../../config/migrate-validate');
 
 module.exports = {
   detectSemanticCommits,
@@ -21,7 +19,6 @@ module.exports = {
   initApis,
   mergeRenovateJson,
   resolvePackageFiles,
-  migrateAndValidate,
 };
 
 async function detectSemanticCommits(config) {
@@ -136,37 +133,6 @@ async function initApis(inputConfig, token) {
   return module.exports.getNpmrc(config);
 }
 
-function migrateAndValidate(config, input) {
-  const { logger } = config;
-  const { isMigrated, migratedConfig } = configMigration.migrateConfig(input);
-  if (isMigrated) {
-    logger.info(
-      { oldConfig: input, newConfig: migratedConfig },
-      'Config migration necessary'
-    );
-  }
-  const massagedConfig = configMassage.massageConfig(migratedConfig);
-  const { warnings, errors } = configValidation.validateConfig(massagedConfig);
-  // istanbul ignore if
-  if (warnings.length) {
-    logger.debug({ warnings }, 'Found renovate config warnings');
-  }
-  if (errors.length) {
-    logger.warn({ errors }, 'Found renovate config errors');
-    /* TODO #556
-    renovateJsonErrors.forEach(error => {
-      config.errors.push(
-        { ...error, ...{ depName: 'renovate.json' } }
-      );
-    }); */
-  }
-  if (!config.repoIsOnboarded) {
-    massagedConfig.warnings = (config.warnings || []).concat(warnings);
-    massagedConfig.errors = (config.errors || []).concat(errors);
-  }
-  return massagedConfig;
-}
-
 // Check for config in `renovate.json`
 async function mergeRenovateJson(config, branchName) {
   const { logger } = config;
diff --git a/test/_fixtures/config/index.js b/test/_fixtures/config/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..c6233c70ad51b67bba816b8f12b5a3a10194dfba
--- /dev/null
+++ b/test/_fixtures/config/index.js
@@ -0,0 +1,9 @@
+const defaultConfig = require('../../../lib/config/defaults').getConfig();
+const logger = require('../logger');
+const api = jest.genMockFromModule('../../../lib/platform/github');
+
+module.exports = {
+  ...defaultConfig,
+  api,
+  logger,
+};
diff --git a/test/config/__snapshots__/migrate-validate.spec.js.snap b/test/config/__snapshots__/migrate-validate.spec.js.snap
new file mode 100644
index 0000000000000000000000000000000000000000..1ac51fbf44ea88d34e6a1d13867c1819d8c5ddbd
--- /dev/null
+++ b/test/config/__snapshots__/migrate-validate.spec.js.snap
@@ -0,0 +1,29 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`config/migrate-validate migrateAndValidate() handles empty 1`] = `
+Object {
+  "errors": Array [],
+  "warnings": Array [],
+}
+`;
+
+exports[`config/migrate-validate migrateAndValidate() handles invalid 1`] = `
+Object {
+  "errors": Array [
+    Object {
+      "depName": "Configuration Error",
+      "message": "Invalid configuration option: \`foo\`",
+    },
+  ],
+  "foo": "none",
+  "warnings": Array [],
+}
+`;
+
+exports[`config/migrate-validate migrateAndValidate() handles migration 1`] = `
+Object {
+  "automerge": false,
+  "errors": Array [],
+  "warnings": Array [],
+}
+`;
diff --git a/test/config/migrate-validate.spec.js b/test/config/migrate-validate.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..81dd2f52d9b2ed6d4e708e1be0d574b6693c9f27
--- /dev/null
+++ b/test/config/migrate-validate.spec.js
@@ -0,0 +1,27 @@
+const { migrateAndValidate } = require('../../lib/config/migrate-validate');
+
+let config;
+beforeEach(() => {
+  jest.resetAllMocks();
+  config = { ...require('../_fixtures/config') };
+});
+
+describe('config/migrate-validate', () => {
+  describe('migrateAndValidate()', () => {
+    it('handles empty', () => {
+      const res = migrateAndValidate(config, {});
+      expect(res).toMatchSnapshot();
+    });
+    it('handles migration', () => {
+      const input = { automerge: 'none' };
+      const res = migrateAndValidate(config, input);
+      expect(res).toMatchSnapshot();
+    });
+    it('handles invalid', () => {
+      const input = { foo: 'none' };
+      const res = migrateAndValidate(config, input);
+      expect(res).toMatchSnapshot();
+      expect(res.errors).toHaveLength(1);
+    });
+  });
+});
diff --git a/test/workers/repository/__snapshots__/apis.spec.js.snap b/test/workers/repository/__snapshots__/apis.spec.js.snap
index ede1b66dc17c718d22b85b1c33b0439fb2c5d83c..8d5fe7bdd6d59bc476da3e683de73167c90338e1 100644
--- a/test/workers/repository/__snapshots__/apis.spec.js.snap
+++ b/test/workers/repository/__snapshots__/apis.spec.js.snap
@@ -1,12 +1,5 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`migrateAndValidate returns empty config 1`] = `
-Object {
-  "errors": Array [],
-  "warnings": Array [],
-}
-`;
-
 exports[`workers/repository/apis checkMonorepos adds lerna packages 1`] = `
 Array [
   "@a/b",
diff --git a/test/workers/repository/apis.spec.js b/test/workers/repository/apis.spec.js
index 180bd9c5df53c6a0cb3942a240478a430296c3d5..5d9ae3fdbe15e2dfa731cd72e0166cffc7c8ed05 100644
--- a/test/workers/repository/apis.spec.js
+++ b/test/workers/repository/apis.spec.js
@@ -319,17 +319,3 @@ describe('workers/repository/apis', () => {
     });
   });
 });
-describe('migrateAndValidate', () => {
-  it('returns empty config', () => {
-    const renovateJson = {};
-    const res = apis.migrateAndValidate(defaultConfig, renovateJson);
-    expect(res).toMatchSnapshot();
-  });
-  it('massages string to array', () => {
-    const renovateJson = {
-      schedule: 'before 5am',
-    };
-    const res = apis.migrateAndValidate(defaultConfig, renovateJson);
-    expect(Array.isArray(res.schedule)).toBe(true);
-  });
-});