diff --git a/lib/index.js b/lib/index.js
index 2ffa1449346c84ad31be2c00994dabf0da9fe2ff..abc9a603bfef74c76377b9714ba3c22a88b48aad 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -136,6 +136,6 @@ async function findPackageFiles(config) {
 async function processPackageFiles(repo) {
   for (const packageFile of repo.packageFiles) {
     const cascadedConfig = configParser.getCascadedConfig(repo, packageFile);
-    await worker(repo.repository, packageFile.fileName, cascadedConfig);
+    await worker.processPackageFile(repo.repository, packageFile.fileName, cascadedConfig);
   }
 }
diff --git a/lib/worker.js b/lib/worker.js
index f916217565ab72a5b3e8ca9cfdc62b2d9c21ffb0..e85a361d31033d6be7697ca887d41e8a3a4776b9 100644
--- a/lib/worker.js
+++ b/lib/worker.js
@@ -11,10 +11,15 @@ const branchWorker = require('./workers/branch');
 
 let config;
 
-module.exports = renovate;
+module.exports = {
+  processPackageFile,
+  findUpgrades,
+  processUpgradesSequentially,
+  updateDependency,
+};
 
 // This function manages the queue per-package file
-async function renovate(repoName, packageFile, packageConfig) {
+async function processPackageFile(repoName, packageFile, packageConfig) {
   // Initialize globals
   config = Object.assign({}, packageConfig);
   config.packageFile = packageFile;
@@ -47,19 +52,19 @@ async function renovate(repoName, packageFile, packageConfig) {
   dependencies =
     dependencies.filter(dependency => config.ignoreDeps.indexOf(dependency.depName) === -1);
   // Find all upgrades for remaining dependencies
-  const upgrades = await findUpgrades(dependencies);
+  const upgrades = await findUpgrades(dependencies, config);
   // Process all upgrades sequentially
-  await processUpgradesSequentially(upgrades);
+  await processUpgradesSequentially(config, upgrades);
   logger.info(`${repoName} ${packageFile} done`);
 }
 
-async function findUpgrades(dependencies) {
+async function findUpgrades(dependencies, inputConfig) {
   const allUpgrades = [];
   // findDepUpgrades can add more than one upgrade to allUpgrades
   async function findDepUpgrades(dep) {
     const npmDependency = await npmApi.getDependency(dep.depName);
     const upgrades =
-      await versionsHelper.determineUpgrades(npmDependency, dep.currentVersion, config);
+      await versionsHelper.determineUpgrades(npmDependency, dep.currentVersion, inputConfig);
     if (upgrades.length > 0) {
       logger.verbose(`${dep.depName}: Upgrades = ${JSON.stringify(upgrades)}`);
       upgrades.forEach((upgrade) => {
@@ -76,7 +81,7 @@ async function findUpgrades(dependencies) {
   return allUpgrades;
 }
 
-async function processUpgradesSequentially(upgrades) {
+async function processUpgradesSequentially(baseConfig, upgrades) {
   if (Object.keys(upgrades).length) {
     logger.verbose('Processing upgrades');
   } else {
@@ -87,15 +92,13 @@ async function processUpgradesSequentially(upgrades) {
   // 1. Reduce chances of GitHub API rate limiting
   // 2. Edge case collision of branch name, e.g. dependency also listed as dev dependency
   for (const upgrade of upgrades) {
-    await updateDependency(upgrade);
+    await module.exports.updateDependency(Object.assign({}, baseConfig, upgrade));
   }
 }
 
 async function updateDependency(upgrade) {
-  // Expand upgrade params
-  const depName = upgrade.depName;
   // Helpers for templating
-  const params = Object.assign({}, config, upgrade);
+  const params = Object.assign({}, upgrade);
   if (upgrade.upgradeType === 'pin') {
     params.isPin = true;
   } else if (upgrade.upgradeType === 'major') {
@@ -104,18 +107,18 @@ async function updateDependency(upgrade) {
     params.isMinor = true;
   }
   // Use templates to generate strings
-  const branchName = handlebars.compile(config.branchName)(params);
-  const prTitle = handlebars.compile(config.prTitle)(params);
+  const branchName = handlebars.compile(params.branchName)(params);
+  const prTitle = handlebars.compile(params.prTitle)(params);
 
   try {
-    if (!config.recreateClosed && await config.api.checkForClosedPr(branchName, prTitle)) {
-      logger.verbose(`Skipping ${depName} upgrade as matching closed PR already existed`);
+    if (!params.recreateClosed && await params.api.checkForClosedPr(branchName, prTitle)) {
+      logger.verbose(`Skipping ${params.depName} upgrade as matching closed PR already existed`);
       return;
     }
     await branchWorker.ensureBranch(params);
     await prWorker.ensurePr(params);
   } catch (error) {
-    logger.error(`Error updating dependency ${depName}: ${error}`);
+    logger.error(`Error updating dependency ${params.depName}: ${error}`);
     // Don't throw here - we don't want to stop the other renovations
   }
 }
diff --git a/test/worker.spec.js b/test/worker.spec.js
index 1564f4fca341961ea02878661baea265b064787d..efa97a014df0649feb6ee821ed22e4db74416cdc 100644
--- a/test/worker.spec.js
+++ b/test/worker.spec.js
@@ -1,5 +1,108 @@
-require('../lib/worker');
+const logger = require('winston');
+const worker = require('../lib/worker');
+const branchWorker = require('../lib/workers/branch');
+const prWorker = require('../lib/workers/pr');
+const defaultConfig = require('../lib/config/defaults').getConfig();
+const npmApi = require('../lib/api/npm');
+const versionsHelper = require('../lib/helpers/versions');
 
-it('placeholder', () => {
-  // TODO: write tests for this module - this is here so the file shows up in coverage
+logger.remove(logger.transports.Console);
+
+jest.mock('../lib/workers/branch');
+jest.mock('../lib/workers/pr');
+jest.mock('../lib/api/npm');
+jest.mock('../lib/helpers/versions');
+
+describe('worker', () => {
+  describe('updateDependency(upgrade)', () => {
+    let config;
+    beforeEach(() => {
+      config = Object.assign({}, defaultConfig);
+      config.api = {
+        checkForClosedPr: jest.fn(),
+      };
+      branchWorker.ensureBranch = jest.fn();
+      prWorker.ensurePr = jest.fn();
+    });
+    it('returns immediately if closed PR found', async () => {
+      config.api.checkForClosedPr.mockReturnValue(true);
+      await worker.updateDependency(config);
+      expect(branchWorker.ensureBranch.mock.calls.length).toBe(0);
+    });
+    it('does not return immediately if recreateClosed true', async () => {
+      config.api.checkForClosedPr.mockReturnValue(true);
+      config.recreateClosed = true;
+      await worker.updateDependency(config);
+      expect(branchWorker.ensureBranch.mock.calls.length).toBe(1);
+    });
+    it('pins', async () => {
+      config.upgradeType = 'pin';
+      await worker.updateDependency(config);
+      expect(branchWorker.ensureBranch.mock.calls.length).toBe(1);
+    });
+    it('majors', async () => {
+      config.upgradeType = 'major';
+      await worker.updateDependency(config);
+      expect(branchWorker.ensureBranch.mock.calls.length).toBe(1);
+    });
+    it('minors', async () => {
+      config.upgradeType = 'minor';
+      await worker.updateDependency(config);
+      expect(branchWorker.ensureBranch.mock.calls.length).toBe(1);
+    });
+    it('handles errors', async () => {
+      config.api.checkForClosedPr = jest.fn(() => {
+        throw new Error('oops');
+      });
+      await worker.updateDependency(config);
+      expect(branchWorker.ensureBranch.mock.calls.length).toBe(0);
+    });
+  });
+  describe('processUpgradesSequentially(baseConfig, upgrades)', () => {
+    let config;
+    beforeEach(() => {
+      config = {};
+      worker.updateDependency = jest.fn();
+    });
+    it('handles zero upgrades', async () => {
+      await worker.processUpgradesSequentially(config, []);
+      expect(worker.updateDependency.mock.calls.length).toBe(0);
+    });
+    it('handles non-zero upgrades', async () => {
+      await worker.processUpgradesSequentially(config, [{}, {}]);
+      expect(worker.updateDependency.mock.calls.length).toBe(2);
+    });
+  });
+  describe('findUpgrades(dependencies, config)', () => {
+    let config;
+    beforeEach(() => {
+      config = {};
+      worker.updateDependency = jest.fn();
+    });
+    it('handles null', async () => {
+      const allUpgrades = await worker.findUpgrades([], config);
+      expect(allUpgrades).toMatchObject([]);
+    });
+    it('handles one dep', async () => {
+      const dep = {
+        depName: 'foo',
+        currentVersion: '1.0.0',
+      };
+      const upgrade = { newVersion: '1.1.0' };
+      npmApi.getDependency = jest.fn(() => ({}));
+      versionsHelper.determineUpgrades = jest.fn(() => [upgrade]);
+      const allUpgrades = await worker.findUpgrades([dep], config);
+      expect(allUpgrades).toMatchObject([Object.assign({}, dep, upgrade)]);
+    });
+    it('handles no upgrades', async () => {
+      const dep = {
+        depName: 'foo',
+        currentVersion: '1.0.0',
+      };
+      npmApi.getDependency = jest.fn(() => ({}));
+      versionsHelper.determineUpgrades = jest.fn(() => []);
+      const allUpgrades = await worker.findUpgrades([dep], config);
+      expect(allUpgrades).toMatchObject([]);
+    });
+  });
 });