diff --git a/lib/datasource/crate/__snapshots__/index.spec.ts.snap b/lib/datasource/crate/__snapshots__/index.spec.ts.snap
index 8cab958d1b4e401393f9a7ea9d3833ac030860d9..63116b90b9060158cf341813e3673154dfc00f09 100644
--- a/lib/datasource/crate/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/crate/__snapshots__/index.spec.ts.snap
@@ -65,6 +65,83 @@ Object {
 }
 `;
 
+exports[`datasource/crate getReleases processes real data: amethyst 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/li/bc/libc",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/am/et/amethyst",
+  },
+]
+`;
+
 exports[`datasource/crate getReleases processes real data: libc 1`] = `
 Object {
   "releases": Array [
@@ -269,4 +346,472 @@ Object {
 }
 `;
 
+exports[`datasource/crate getReleases processes real data: libc 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/li/bc/libc",
+  },
+]
+`;
+
+exports[`datasource/crate getReleases returns null for 404 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+]
+`;
+
+exports[`datasource/crate getReleases returns null for empty list 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+]
+`;
+
+exports[`datasource/crate getReleases returns null for empty result 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+]
+`;
+
+exports[`datasource/crate getReleases returns null for invalid crate data 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/li/bc/libc",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/am/et/amethyst",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/in/va/invalid-crate-name",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+]
+`;
+
+exports[`datasource/crate getReleases returns null for missing fields 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+]
+`;
+
+exports[`datasource/crate getReleases returns null for unknown error 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+]
+`;
+
+exports[`datasource/crate getReleases returns null if crate name is invalid 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/li/bc/libc",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/am/et/amethyst",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/in/va/invalid-crate-name",
+  },
+]
+`;
+
 exports[`datasource/crate getReleases throws for 5xx 1`] = `[Error: registry-failure]`;
+
+exports[`datasource/crate getReleases throws for 5xx 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/no/n_/non_existent_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "raw.githubusercontent.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://raw.githubusercontent.com/rust-lang/crates.io-index/master/so/me/some_crate",
+  },
+]
+`;
diff --git a/lib/datasource/crate/index.spec.ts b/lib/datasource/crate/index.spec.ts
index d30952f093532c3ffd566e6a87e615d065aed73d..bd9708266aa12f0790d9ca5ccc0a66c32f3491d3 100644
--- a/lib/datasource/crate/index.spec.ts
+++ b/lib/datasource/crate/index.spec.ts
@@ -1,10 +1,8 @@
 import fs from 'fs';
+import * as httpMock from '../../../test/httpMock';
 
-import _got from '../../util/got';
 import { getReleases } from '.';
 
-const got: any = _got;
-
 const res1 = fs.readFileSync('lib/datasource/crate/__fixtures__/libc', 'utf8');
 const res2 = fs.readFileSync(
   'lib/datasource/crate/__fixtures__/amethyst',
@@ -15,46 +13,42 @@ const res3 = fs.readFileSync(
   'utf8'
 );
 
-jest.mock('../../util/got');
+const baseUrl =
+  'https://raw.githubusercontent.com/rust-lang/crates.io-index/master/';
 
 describe('datasource/crate', () => {
   describe('getReleases', () => {
     it('returns null for empty result', async () => {
-      got.mockReturnValueOnce(null);
+      httpMock.scope(baseUrl).get('/no/n_/non_existent_crate').reply(200, {});
       expect(
         await getReleases({ lookupName: 'non_existent_crate' })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for missing fields', async () => {
-      got.mockReturnValueOnce({
-        body: undefined,
-      });
+      httpMock
+        .scope(baseUrl)
+        .get('/no/n_/non_existent_crate')
+        .reply(200, undefined);
       expect(
         await getReleases({ lookupName: 'non_existent_crate' })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for empty list', async () => {
-      got.mockReturnValueOnce({
-        body: '\n',
-      });
+      httpMock.scope(baseUrl).get('/no/n_/non_existent_crate').reply(200, '\n');
       expect(
         await getReleases({ lookupName: 'non_existent_crate' })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for 404', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 404,
-        })
-      );
+      httpMock.scope(baseUrl).get('/so/me/some_crate').reply(404);
       expect(await getReleases({ lookupName: 'some_crate' })).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('throws for 5xx', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 502,
-        })
-      );
+      httpMock.scope(baseUrl).get('/so/me/some_crate').reply(502);
       let e;
       try {
         await getReleases({ lookupName: 'some_crate' });
@@ -63,44 +57,40 @@ describe('datasource/crate', () => {
       }
       expect(e).toBeDefined();
       expect(e).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for unknown error', async () => {
-      got.mockImplementationOnce(() => {
-        throw new Error();
-      });
+      httpMock.scope(baseUrl).get('/so/me/some_crate').replyWithError('');
       expect(await getReleases({ lookupName: 'some_crate' })).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real data: libc', async () => {
-      got.mockReturnValueOnce({
-        body: res1,
-      });
+      httpMock.scope(baseUrl).get('/li/bc/libc').reply(200, res1);
       const res = await getReleases({ lookupName: 'libc' });
       expect(res).toMatchSnapshot();
       expect(res).not.toBeNull();
       expect(res).toBeDefined();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real data: amethyst', async () => {
-      got.mockReturnValueOnce({
-        body: res2,
-      });
+      httpMock.scope(baseUrl).get('/am/et/amethyst').reply(200, res2);
       const res = await getReleases({ lookupName: 'amethyst' });
       expect(res).toMatchSnapshot();
       expect(res).not.toBeNull();
       expect(res).toBeDefined();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null if crate name is invalid', async () => {
-      got.mockReturnValueOnce({
-        body: res2,
-      });
+      httpMock.scope(baseUrl).get('/in/va/invalid-crate-name').reply(200, res2);
       const res = await getReleases({ lookupName: 'invalid-crate-name' });
       expect(res).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for invalid crate data', async () => {
-      got.mockReturnValueOnce({
-        body: res3,
-      });
+      httpMock.scope(baseUrl).get('/so/me/some_crate').reply(200, res3);
       const res = await getReleases({ lookupName: 'some_crate' });
       expect(res).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
   });
 });
diff --git a/lib/datasource/dart/__snapshots__/index.spec.ts.snap b/lib/datasource/dart/__snapshots__/index.spec.ts.snap
index f293d01d44538373549879efa18fdf524ee39022..52477f48413e04c25a4d8f5061c8f6e5119796b3 100644
--- a/lib/datasource/dart/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/dart/__snapshots__/index.spec.ts.snap
@@ -60,4 +60,104 @@ Object {
 }
 `;
 
+exports[`datasource/dart getReleases processes real data 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "pub.dartlang.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://pub.dartlang.org/api/packages/shared_preferences",
+  },
+]
+`;
+
+exports[`datasource/dart getReleases returns null for 404 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "pub.dartlang.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://pub.dartlang.org/api/packages/shared_preferences",
+  },
+]
+`;
+
+exports[`datasource/dart getReleases returns null for empty fields 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "pub.dartlang.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://pub.dartlang.org/api/packages/shared_preferences",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "pub.dartlang.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://pub.dartlang.org/api/packages/shared_preferences",
+  },
+]
+`;
+
+exports[`datasource/dart getReleases returns null for empty result 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "pub.dartlang.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://pub.dartlang.org/api/packages/non_sense",
+  },
+]
+`;
+
+exports[`datasource/dart getReleases returns null for unknown error 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "pub.dartlang.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://pub.dartlang.org/api/packages/shared_preferences",
+  },
+]
+`;
+
 exports[`datasource/dart getReleases throws for 5xx 1`] = `[Error: registry-failure]`;
+
+exports[`datasource/dart getReleases throws for 5xx 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "pub.dartlang.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://pub.dartlang.org/api/packages/shared_preferences",
+  },
+]
+`;
diff --git a/lib/datasource/dart/index.spec.ts b/lib/datasource/dart/index.spec.ts
index 95124ba2e25f1e2e8ed8fed89ccfde50bb3b982f..317274b6ea931d7f043a205fce738d1b3e388ec7 100644
--- a/lib/datasource/dart/index.spec.ts
+++ b/lib/datasource/dart/index.spec.ts
@@ -1,9 +1,7 @@
 import fs from 'fs';
-import _got from '../../util/got';
+import * as httpMock from '../../../test/httpMock';
 import { getReleases } from '.';
 
-const got: any = _got;
-
 const body: any = JSON.parse(
   fs.readFileSync(
     'lib/datasource/dart/__fixtures__/shared_preferences.json',
@@ -11,20 +9,32 @@ const body: any = JSON.parse(
   )
 );
 
-jest.mock('../../util/got');
+const baseUrl = 'https://pub.dartlang.org/api/packages/';
 
 describe('datasource/dart', () => {
+  beforeEach(() => {
+    httpMock.setup();
+  });
+
+  afterEach(() => {
+    httpMock.reset();
+  });
+
   describe('getReleases', () => {
     it('returns null for empty result', async () => {
-      got.mockReturnValueOnce(null);
+      httpMock.scope(baseUrl).get('/non_sense').reply(200, null);
       expect(await getReleases({ lookupName: 'non_sense' })).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for empty fields', async () => {
       const withoutVersions = {
         ...body,
         versions: undefined,
       };
-      got.mockReturnValueOnce({ body: withoutVersions });
+      httpMock
+        .scope(baseUrl)
+        .get('/shared_preferences')
+        .reply(200, withoutVersions);
       expect(
         await getReleases({ lookupName: 'shared_preferences' })
       ).toBeNull();
@@ -33,27 +43,25 @@ describe('datasource/dart', () => {
         ...body,
         latest: undefined,
       };
-      got.mockReturnValueOnce({ body: withoutLatest });
+      httpMock
+        .scope(baseUrl)
+        .get('/shared_preferences')
+        .reply(200, withoutLatest);
       expect(
         await getReleases({ lookupName: 'shared_preferences' })
       ).toBeNull();
+
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for 404', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 404,
-        })
-      );
+      httpMock.scope(baseUrl).get('/shared_preferences').reply(404);
       expect(
         await getReleases({ lookupName: 'shared_preferences' })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('throws for 5xx', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 502,
-        })
-      );
+      httpMock.scope(baseUrl).get('/shared_preferences').reply(502);
       let e;
       try {
         await getReleases({ lookupName: 'shared_preferences' });
@@ -62,21 +70,22 @@ describe('datasource/dart', () => {
       }
       expect(e).toBeDefined();
       expect(e).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for unknown error', async () => {
-      got.mockImplementationOnce(() => {
-        throw new Error();
-      });
+      httpMock.scope(baseUrl).get('/shared_preferences').replyWithError('');
       expect(
         await getReleases({ lookupName: 'shared_preferences' })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real data', async () => {
-      got.mockReturnValueOnce({ body });
+      httpMock.scope(baseUrl).get('/shared_preferences').reply(200, body);
       const res = await getReleases({
         lookupName: 'shared_preferences',
       });
       expect(res).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
   });
 });
diff --git a/lib/datasource/docker/index.spec.ts b/lib/datasource/docker/index.spec.ts
index 6ca136f7d126d84450be735aa325f3839e9b3f7f..ebf61ddf0dc01dd1aef093810b256ad2a069ec51 100644
--- a/lib/datasource/docker/index.spec.ts
+++ b/lib/datasource/docker/index.spec.ts
@@ -486,7 +486,6 @@ describe('api/docker', () => {
         datasource: docker.id,
         depName: 'k8s.gcr.io/kubernetes-dashboard-amd64',
       });
-      httpMock.getTrace();
       expect(res.releases).toHaveLength(1);
       expect(httpMock.getTrace()).toMatchSnapshot();
     });
diff --git a/lib/datasource/galaxy/index.spec.ts b/lib/datasource/galaxy/index.spec.ts
index 046fe2a05df8f27b311620a3eb107a3af20a139d..8558281257b1d88c2e1a59a9e690ab4b9b466647 100644
--- a/lib/datasource/galaxy/index.spec.ts
+++ b/lib/datasource/galaxy/index.spec.ts
@@ -87,7 +87,6 @@ describe('datasource/galaxy', () => {
         .get('/api/v1/roles/?owner__username=foo&name=bar')
         .reply(200, empty);
       const res = await getReleases({ lookupName: 'foo.bar' });
-      httpMock.getTrace();
       expect(res).toBeNull();
       expect(httpMock.getTrace()).toMatchSnapshot();
     });
diff --git a/lib/datasource/gitlab-tags/index.spec.ts b/lib/datasource/gitlab-tags/index.spec.ts
index c7f4a9af5393fe0bb6c46fc93cf87bbe985d775b..17f10660dfb77e572323fa39c2bef02c44b2eb1b 100644
--- a/lib/datasource/gitlab-tags/index.spec.ts
+++ b/lib/datasource/gitlab-tags/index.spec.ts
@@ -31,7 +31,6 @@ describe('datasource/gitlab-tags', () => {
         registryUrls: ['https://gitlab.company.com/api/v4/'],
         lookupName: 'some/dep2',
       });
-      httpMock.getTrace();
       expect(res).toMatchSnapshot();
       expect(res.releases).toHaveLength(3);
       expect(httpMock.getTrace()).toMatchSnapshot();
diff --git a/lib/datasource/helm/__snapshots__/index.spec.ts.snap b/lib/datasource/helm/__snapshots__/index.spec.ts.snap
index ce20b09edf64d758dbd1473a08326451ee01e3ae..d0ed6d6ecf767604aa09b4407911a1128e2a70c0 100644
--- a/lib/datasource/helm/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/helm/__snapshots__/index.spec.ts.snap
@@ -1,5 +1,19 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`datasource/helm getReleases adds trailing slash to subdirectories 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "example-repository.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://example-repository.com/subdir/index.yaml",
+  },
+]
+`;
+
 exports[`datasource/helm getReleases returns list of versions for normal response 1`] = `
 Object {
   "homepage": "https://www.getambassador.io/",
@@ -118,4 +132,130 @@ Object {
 }
 `;
 
+exports[`datasource/helm getReleases returns list of versions for normal response 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "example-repository.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://example-repository.com/index.yaml",
+  },
+]
+`;
+
+exports[`datasource/helm getReleases returns null for 404 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "example-repository.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://example-repository.com/index.yaml",
+  },
+]
+`;
+
+exports[`datasource/helm getReleases returns null for empty response 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "example-repository.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://example-repository.com/index.yaml",
+  },
+]
+`;
+
+exports[`datasource/helm getReleases returns null for missing response body 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "example-repository.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://example-repository.com/index.yaml",
+  },
+]
+`;
+
+exports[`datasource/helm getReleases returns null for unknown error 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "example-repository.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://example-repository.com/index.yaml",
+  },
+]
+`;
+
+exports[`datasource/helm getReleases returns null if index.yaml in response is empty 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "example-repository.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://example-repository.com/index.yaml",
+  },
+]
+`;
+
+exports[`datasource/helm getReleases returns null if index.yaml in response is invalid 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "example-repository.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://example-repository.com/index.yaml",
+  },
+]
+`;
+
+exports[`datasource/helm getReleases returns null if lookupName is not in index.yaml 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "example-repository.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://example-repository.com/index.yaml",
+  },
+]
+`;
+
 exports[`datasource/helm getReleases throws for 5xx 1`] = `[Error: registry-failure]`;
+
+exports[`datasource/helm getReleases throws for 5xx 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "example-repository.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://example-repository.com/index.yaml",
+  },
+]
+`;
diff --git a/lib/datasource/helm/index.spec.ts b/lib/datasource/helm/index.spec.ts
index b86199f2450e5c15e4c7b9b1f24d161e054dc88f..671643ba06f801e58bf24eb015faaf738c19dd82 100644
--- a/lib/datasource/helm/index.spec.ts
+++ b/lib/datasource/helm/index.spec.ts
@@ -1,22 +1,24 @@
 import fs from 'fs';
-import _got from '../../util/got';
+import * as httpMock from '../../../test/httpMock';
 import { getReleases } from '.';
 
-const got: any = _got;
-
 // Truncated index.yaml file
 const indexYaml = fs.readFileSync(
   'lib/datasource/helm/__fixtures__/index.yaml',
   'utf8'
 );
 
-jest.mock('../../util/got');
-
 describe('datasource/helm', () => {
   describe('getReleases', () => {
     beforeEach(() => {
       jest.resetAllMocks();
+      httpMock.setup();
+    });
+
+    afterEach(() => {
+      httpMock.reset();
     });
+
     it('returns null if lookupName was not provided', async () => {
       expect(
         await getReleases({
@@ -34,44 +36,49 @@ describe('datasource/helm', () => {
       ).toBeNull();
     });
     it('returns null for empty response', async () => {
-      got.mockReturnValueOnce(null);
+      httpMock
+        .scope('https://example-repository.com')
+        .get('/index.yaml')
+        .reply(200, null);
       expect(
         await getReleases({
           lookupName: 'non_existent_chart',
           registryUrls: ['https://example-repository.com'],
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for missing response body', async () => {
-      got.mockReturnValueOnce({
-        body: undefined,
-      });
+      httpMock
+        .scope('https://example-repository.com')
+        .get('/index.yaml')
+        .reply(200, undefined);
       expect(
         await getReleases({
           lookupName: 'non_existent_chart',
           registryUrls: ['https://example-repository.com'],
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for 404', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 404,
-        })
-      );
+      httpMock
+        .scope('https://example-repository.com')
+        .get('/index.yaml')
+        .reply(404);
       expect(
         await getReleases({
           lookupName: 'some_chart',
           registryUrls: ['https://example-repository.com'],
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('throws for 5xx', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 502,
-        })
-      );
+      httpMock
+        .scope('https://example-repository.com')
+        .get('/index.yaml')
+        .reply(502);
       let e;
       try {
         await getReleases({
@@ -83,26 +90,32 @@ describe('datasource/helm', () => {
       }
       expect(e).toBeDefined();
       expect(e).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for unknown error', async () => {
-      got.mockImplementationOnce(() => {
-        throw new Error();
-      });
+      httpMock
+        .scope('https://example-repository.com')
+        .get('/index.yaml')
+        .replyWithError('');
       expect(
         await getReleases({
           lookupName: 'some_chart',
           registryUrls: ['https://example-repository.com'],
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null if index.yaml in response is empty', async () => {
-      const res = { body: '# A comment' };
-      got.mockReturnValueOnce(res);
+      httpMock
+        .scope('https://example-repository.com')
+        .get('/index.yaml')
+        .reply(200, '# A comment');
       const releases = await getReleases({
         lookupName: 'non_existent_chart',
         registryUrls: ['https://example-repository.com'],
       });
       expect(releases).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null if index.yaml in response is invalid', async () => {
       const res = {
@@ -111,39 +124,56 @@ describe('datasource/helm', () => {
                      [
                      yaml`,
       };
-      got.mockReturnValueOnce(res);
+      httpMock
+        .scope('https://example-repository.com')
+        .get('/index.yaml')
+        .reply(200, res);
       const releases = await getReleases({
         lookupName: 'non_existent_chart',
         registryUrls: ['https://example-repository.com'],
       });
       expect(releases).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null if lookupName is not in index.yaml', async () => {
-      got.mockReturnValueOnce({ body: indexYaml });
+      httpMock
+        .scope('https://example-repository.com')
+        .get('/index.yaml')
+        .reply(200, indexYaml);
       const releases = await getReleases({
         lookupName: 'non_existent_chart',
         registryUrls: ['https://example-repository.com'],
       });
       expect(releases).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns list of versions for normal response', async () => {
-      got.mockReturnValueOnce({ body: indexYaml });
+      httpMock
+        .scope('https://example-repository.com')
+        .get('/index.yaml')
+        .reply(200, indexYaml);
       const releases = await getReleases({
         lookupName: 'ambassador',
         registryUrls: ['https://example-repository.com'],
       });
       expect(releases).not.toBeNull();
       expect(releases).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('adds trailing slash to subdirectories', async () => {
-      got.mockReturnValueOnce({ body: indexYaml });
+      httpMock
+        .scope('https://example-repository.com')
+        .get('/subdir/index.yaml')
+        .reply(200, indexYaml);
       await getReleases({
         lookupName: 'ambassador',
         registryUrls: ['https://example-repository.com/subdir'],
       });
-      expect(got.mock.calls[0][0]).toEqual(
+      const trace = httpMock.getTrace();
+      expect(trace[0].url).toEqual(
         'https://example-repository.com/subdir/index.yaml'
       );
+      expect(trace).toMatchSnapshot();
     });
   });
 });
diff --git a/lib/datasource/hex/__snapshots__/index.spec.ts.snap b/lib/datasource/hex/__snapshots__/index.spec.ts.snap
index 668e54458d8bccb5c864d52f1d2c052d06840bb9..44a0e16be30f1228c9c14e56d9bf165d3707d230 100644
--- a/lib/datasource/hex/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/hex/__snapshots__/index.spec.ts.snap
@@ -88,6 +88,21 @@ Object {
 }
 `;
 
+exports[`datasource/hex getReleases process public repo without auth 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "hex.pm",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://hex.pm/api/packages/certifi",
+  },
+]
+`;
+
 exports[`datasource/hex getReleases processes real data 1`] = `
 Object {
   "homepage": "https://hex.pm/packages/certifi",
@@ -175,3 +190,139 @@ Object {
   "sourceUrl": "https://github.com/certifi/erlang-certifi",
 }
 `;
+
+exports[`datasource/hex getReleases processes real data 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "hex.pm",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://hex.pm/api/packages/certifi",
+  },
+]
+`;
+
+exports[`datasource/hex getReleases returns null for 401 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "hex.pm",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://hex.pm/api/packages/some_package",
+  },
+]
+`;
+
+exports[`datasource/hex getReleases returns null for 404 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "hex.pm",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://hex.pm/api/packages/some_package",
+  },
+]
+`;
+
+exports[`datasource/hex getReleases returns null for empty result 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "hex.pm",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://hex.pm/api/packages/non_existent_package",
+  },
+]
+`;
+
+exports[`datasource/hex getReleases returns null for missing fields 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "hex.pm",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://hex.pm/api/packages/non_existent_package",
+  },
+]
+`;
+
+exports[`datasource/hex getReleases returns null for unknown error 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "hex.pm",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://hex.pm/api/packages/some_package",
+  },
+]
+`;
+
+exports[`datasource/hex getReleases returns null with wrong auth token 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer this_simple_token",
+      "host": "hex.pm",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://hex.pm/api/packages/certifi",
+  },
+]
+`;
+
+exports[`datasource/hex getReleases throws for 5xx 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "hex.pm",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://hex.pm/api/packages/some_crate",
+  },
+]
+`;
+
+exports[`datasource/hex getReleases throws for 429 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "hex.pm",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://hex.pm/api/packages/some_crate",
+  },
+]
+`;
diff --git a/lib/datasource/hex/index.spec.ts b/lib/datasource/hex/index.spec.ts
index b0d8e3e51a919506fe5dadf9251bfe566ae3e297..8c439c343d7ecbaf66fbf3dd2e07c107a4cce547 100644
--- a/lib/datasource/hex/index.spec.ts
+++ b/lib/datasource/hex/index.spec.ts
@@ -1,10 +1,9 @@
 import fs from 'fs';
+import * as httpMock from '../../../test/httpMock';
 import { DATASOURCE_FAILURE } from '../../constants/error-messages';
-import _got from '../../util/got';
 import * as _hostRules from '../../util/host-rules';
 import { getReleases } from '.';
 
-const got: any = _got;
 const hostRules: any = _hostRules;
 
 let res1 = fs.readFileSync(
@@ -13,98 +12,86 @@ let res1 = fs.readFileSync(
 );
 res1 = JSON.parse(res1);
 
-jest.mock('../../util/got');
 jest.mock('../../util/host-rules');
 
+const baseUrl = 'https://hex.pm/api/packages/';
+
 describe('datasource/hex', () => {
+  beforeEach(() => {
+    httpMock.setup();
+  });
+
+  afterEach(() => {
+    httpMock.reset();
+  });
+
   describe('getReleases', () => {
     it('returns null for empty result', async () => {
-      got.mockReturnValueOnce(null);
+      httpMock.scope(baseUrl).get('/non_existent_package').reply(200, null);
       expect(
         await getReleases({ lookupName: 'non_existent_package' })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for missing fields', async () => {
-      got.mockReturnValueOnce({});
-      expect(
-        await getReleases({ lookupName: 'non_existent_package' })
-      ).toBeNull();
-
-      got.mockReturnValueOnce({ body: {} });
+      httpMock.scope(baseUrl).get('/non_existent_package').reply(200, {});
       expect(
         await getReleases({ lookupName: 'non_existent_package' })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for 404', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 404,
-        })
-      );
+      httpMock.scope(baseUrl).get('/some_package').reply(404);
       expect(await getReleases({ lookupName: 'some_package' })).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for 401', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 401,
-        })
-      );
+      httpMock.scope(baseUrl).get('/some_package').reply(401);
       expect(await getReleases({ lookupName: 'some_package' })).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('throws for 429', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 429,
-        })
-      );
+      httpMock.scope(baseUrl).get('/some_crate').reply(429);
       await expect(getReleases({ lookupName: 'some_crate' })).rejects.toThrow(
         DATASOURCE_FAILURE
       );
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('throws for 5xx', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 502,
-        })
-      );
+      httpMock.scope(baseUrl).get('/some_crate').reply(502);
       await expect(getReleases({ lookupName: 'some_crate' })).rejects.toThrow(
         DATASOURCE_FAILURE
       );
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for unknown error', async () => {
-      got.mockImplementationOnce(() => {
-        throw new Error();
-      });
+      httpMock.scope(baseUrl).get('/some_package').replyWithError('');
       expect(await getReleases({ lookupName: 'some_package' })).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null with wrong auth token', async () => {
+      httpMock.scope(baseUrl).get('/certifi').reply(401);
       hostRules.find.mockReturnValueOnce({ token: 'this_simple_token' });
-      got.mockReturnValueOnce(
-        Promise.reject({
-          statusCode: 401,
-        })
-      );
       const res = await getReleases({ lookupName: 'certifi' });
       expect(res).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real data', async () => {
-      got.mockReturnValueOnce({
-        body: res1,
-      });
+      httpMock.scope(baseUrl).get('/certifi').reply(200, res1);
       const res = await getReleases({ lookupName: 'certifi' });
       expect(res).toMatchSnapshot();
       expect(res).not.toBeNull();
       expect(res).toBeDefined();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('process public repo without auth', async () => {
+      httpMock.scope(baseUrl).get('/certifi').reply(200, res1);
       hostRules.find.mockReturnValueOnce({});
-      got.mockReturnValueOnce({
-        body: res1,
-      });
       const res = await getReleases({ lookupName: 'certifi' });
       expect(res).toMatchSnapshot();
       expect(res).not.toBeNull();
       expect(res).toBeDefined();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
   });
 });
diff --git a/lib/datasource/npm/__snapshots__/get.spec.ts.snap b/lib/datasource/npm/__snapshots__/get.spec.ts.snap
new file mode 100644
index 0000000000000000000000000000000000000000..579efc3629c3591688bf42e2ba44103b504b1a22
--- /dev/null
+++ b/lib/datasource/npm/__snapshots__/get.spec.ts.snap
@@ -0,0 +1,484 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`datasource/npm/get cover all paths 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer XXX",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/none",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer XXX",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer XXX",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest2",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer XXX",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/error-401",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer XXX",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/error-402",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer XXX",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/error-404",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer XXX",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/error4",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "cache-control": "no-cache",
+      "host": "registry.npmjs.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://registry.npmjs.org/npm-parse-error",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "cache-control": "no-cache",
+      "host": "registry.npmjs.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://registry.npmjs.org/npm-error-402",
+  },
+]
+`;
+
+exports[`datasource/npm/get has basic auth "@myco:registry=https://test.org
+//test.org/:_auth=dGVzdDp0ZXN0" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Basic dGVzdDp0ZXN0",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get has basic auth "@myco:registry=https://test.org
+_auth=dGVzdDp0ZXN0" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Basic dGVzdDp0ZXN0",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get has basic auth "registry=https://test.org
+//test.org/:_auth=dGVzdDp0ZXN0" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Basic dGVzdDp0ZXN0",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get has basic auth "registry=https://test.org
+//test.org/:username=test
+//test.org/:_password=dGVzdA==" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Basic dGVzdDp0ZXN0",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get has basic auth "registry=https://test.org
+_auth=dGVzdDp0ZXN0" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Basic dGVzdDp0ZXN0",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get has basic auth "registry=https://test.org
+_auth=dGVzdDp0ZXN0" 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Basic dGVzdDp0ZXN0",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get has basic auth "registry=https://test.org/sub
+//test.org/:_auth=dGVzdDp0ZXN0" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Basic dGVzdDp0ZXN0",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/sub/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get has basic auth "registry=https://test.org/sub
+//test.org/sub/:_auth=dGVzdDp0ZXN0" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Basic dGVzdDp0ZXN0",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/sub/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get has basic auth "registry=https://test.org/sub
+_auth=dGVzdDp0ZXN0" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Basic dGVzdDp0ZXN0",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/sub/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get has bearer auth "@myco:registry=https://test.org
+//test.org/:_authToken=XXX" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer XXX",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get has bearer auth "registry=https://test.org
+//test.org/:_authToken=XXX" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer XXX",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get has bearer auth "registry=https://test.org
+_authToken=XXX" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer XXX",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get has bearer auth "registry=https://test.org
+_authToken=XXX" 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer XXX",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get has bearer auth "registry=https://test.org/sub
+//test.org/:_authToken=XXX" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer XXX",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/sub/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get has bearer auth "registry=https://test.org/sub
+//test.org/sub/:_authToken=XXX" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer XXX",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/sub/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get has bearer auth "registry=https://test.org/sub
+_authToken=XXX" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Bearer XXX",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/sub/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get no auth "@myco:registry=https://test.org
+//test.org/sub/:_auth=dGVzdDp0ZXN0" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get no auth "@myco:registry=https://test.org
+//test.org/sub/:_authToken=XXX" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get no auth "@myco:registry=https://test.org
+_authToken=XXX" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get no auth "@myco:registry=https://test.org" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest",
+  },
+]
+`;
+
+exports[`datasource/npm/get no auth "registry=https://test.org" 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "cache-control": "no-cache",
+      "host": "test.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://test.org/@myco%2Ftest",
+  },
+]
+`;
diff --git a/lib/datasource/npm/get.spec.ts b/lib/datasource/npm/get.spec.ts
index b18f5fd3effb3d12840785132e1f96efc7ee723d..2d394b010d4ade2c13ebeac2b81fe5c46868fcca 100644
--- a/lib/datasource/npm/get.spec.ts
+++ b/lib/datasource/npm/get.spec.ts
@@ -1,25 +1,24 @@
-import got from 'got';
-import { getName, partial } from '../../../test/util';
-import * as _got from '../../util/got';
+import * as httpMock from '../../../test/httpMock';
+import { getName } from '../../../test/util';
 import { DatasourceError } from '../common';
 import { getDependency, resetMemCache } from './get';
 import { setNpmrc } from './npmrc';
 
-jest.mock('../../util/got');
-
-const api: jest.Mock<got.GotPromise<object>> = _got.api as never;
+function getPath(s = ''): string {
+  const [x] = s.split('\n');
+  const prePath = x.replace(/^.*https:\/\/test\.org/, '');
+  return `${prePath}/@myco%2Ftest`;
+}
 
 describe(getName(__filename), () => {
-  function mock(body: object): void {
-    api.mockResolvedValueOnce(
-      partial<got.Response<object>>({ body })
-    );
-  }
-
   beforeEach(() => {
     jest.clearAllMocks();
     resetMemCache();
-    mock({ body: { name: '@myco/test' } });
+    httpMock.setup();
+  });
+
+  afterEach(() => {
+    httpMock.reset();
   });
 
   describe('has bearer auth', () => {
@@ -34,11 +33,18 @@ describe(getName(__filename), () => {
     ];
 
     it.each(configs)('%p', async (npmrc) => {
-      expect.assertions(1);
+      expect.assertions(2);
+      httpMock
+        .scope('https://test.org')
+        .get(getPath(npmrc))
+        .reply(200, { name: '@myco/test' });
+
       setNpmrc(npmrc);
       await getDependency('@myco/test', 0);
 
-      expect(api.mock.calls[0][1].headers.authorization).toEqual('Bearer XXX');
+      const trace = httpMock.getTrace();
+      expect(trace[0].headers.authorization).toEqual('Bearer XXX');
+      expect(trace).toMatchSnapshot();
     });
   });
 
@@ -56,13 +62,17 @@ describe(getName(__filename), () => {
     ];
 
     it.each(configs)('%p', async (npmrc) => {
-      expect.assertions(1);
+      expect.assertions(2);
+      httpMock
+        .scope('https://test.org')
+        .get(getPath(npmrc))
+        .reply(200, { name: '@myco/test' });
       setNpmrc(npmrc);
       await getDependency('@myco/test', 0);
 
-      expect(api.mock.calls[0][1].headers.authorization).toEqual(
-        'Basic dGVzdDp0ZXN0'
-      );
+      const trace = httpMock.getTrace();
+      expect(trace[0].headers.authorization).toEqual('Basic dGVzdDp0ZXN0');
+      expect(trace).toMatchSnapshot();
     });
   });
 
@@ -76,53 +86,79 @@ describe(getName(__filename), () => {
     ];
 
     it.each(configs)('%p', async (npmrc) => {
-      expect.assertions(1);
+      expect.assertions(2);
+      httpMock
+        .scope('https://test.org')
+        .get(getPath(npmrc))
+        .reply(200, { name: '@myco/test' });
       setNpmrc(npmrc);
       await getDependency('@myco/test', 0);
 
-      expect(api.mock.calls[0][1].headers.authorization).toBeUndefined();
+      const trace = httpMock.getTrace();
+      expect(trace[0].headers.authorization).toBeUndefined();
+      expect(trace).toMatchSnapshot();
     });
   });
 
   it('cover all paths', async () => {
-    expect.assertions(9);
+    expect.assertions(10);
 
     setNpmrc('registry=https://test.org\n_authToken=XXX');
 
+    httpMock
+      .scope('https://test.org')
+      .get('/none')
+      .reply(200, { name: '@myco/test' });
     expect(await getDependency('none', 0)).toBeNull();
 
-    mock({
-      name: '@myco/test',
-      repository: {},
-      versions: { '1.0.0': {} },
-      'dist-tags': { latest: '1.0.0' },
-    });
+    httpMock
+      .scope('https://test.org')
+      .get('/@myco%2Ftest')
+      .reply(200, {
+        name: '@myco/test',
+        repository: {},
+        versions: { '1.0.0': {} },
+        'dist-tags': { latest: '1.0.0' },
+      });
     expect(await getDependency('@myco/test', 0)).toBeDefined();
 
-    mock({
-      name: '@myco/test2',
-      versions: { '1.0.0': {} },
-      'dist-tags': { latest: '1.0.0' },
-    });
+    httpMock
+      .scope('https://test.org')
+      .get('/@myco%2Ftest2')
+      .reply(200, {
+        name: '@myco/test2',
+        versions: { '1.0.0': {} },
+        'dist-tags': { latest: '1.0.0' },
+      });
     expect(await getDependency('@myco/test2', 0)).toBeDefined();
 
-    api.mockRejectedValueOnce({ statusCode: 401 });
+    httpMock.scope('https://test.org').get('/error-401').reply(401);
     expect(await getDependency('error-401', 0)).toBeNull();
-    api.mockRejectedValueOnce({ statusCode: 402 });
+
+    httpMock.scope('https://test.org').get('/error-402').reply(402);
     expect(await getDependency('error-402', 0)).toBeNull();
-    api.mockRejectedValueOnce({ statusCode: 404 });
+
+    httpMock.scope('https://test.org').get('/error-404').reply(404);
     expect(await getDependency('error-404', 0)).toBeNull();
 
-    api.mockRejectedValueOnce({});
+    httpMock.scope('https://test.org').get('/error4').reply(200, null);
     expect(await getDependency('error4', 0)).toBeNull();
 
     setNpmrc();
-    api.mockRejectedValueOnce({ name: 'ParseError', body: 'parse-error' });
+    httpMock
+      .scope('https://registry.npmjs.org')
+      .get('/npm-parse-error')
+      .reply(200, 'not-a-json');
     await expect(getDependency('npm-parse-error', 0)).rejects.toThrow(
       DatasourceError
     );
 
-    api.mockRejectedValueOnce({ statusCode: 402 });
+    httpMock
+      .scope('https://registry.npmjs.org')
+      .get('/npm-error-402')
+      .reply(402);
     expect(await getDependency('npm-error-402', 0)).toBeNull();
+
+    expect(httpMock.getTrace()).toMatchSnapshot();
   });
 });
diff --git a/lib/datasource/npm/get.ts b/lib/datasource/npm/get.ts
index f465675b7a8edd95c1cb33e9a1000dc52d15612e..d2f0e2ab362cc62d063f056b94141fb74583c683 100644
--- a/lib/datasource/npm/get.ts
+++ b/lib/datasource/npm/get.ts
@@ -276,6 +276,7 @@ export async function getDependency(
         await delay(1000 * delaySeconds);
         return getDependency(packageName, retries - 1);
       }
+      // istanbul ignore if
       if (err.name === 'ParseError' && err.body) {
         err.body = 'err.body deleted by Renovate';
       }
diff --git a/lib/datasource/nuget/__snapshots__/index.spec.ts.snap b/lib/datasource/nuget/__snapshots__/index.spec.ts.snap
index ff43c28a2fddd7fb6f1f533303078b7d36640dd5..730bf6e187ff088676a8dd2382b1d06b2e6863f0 100644
--- a/lib/datasource/nuget/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/nuget/__snapshots__/index.spec.ts.snap
@@ -1,5 +1,70 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`datasource/nuget getReleases can't get packages list (v3) 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api.nuget.org/v3/index.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api-v2v3search-0.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api-v2v3search-0.nuget.org/query?q=PackageId:nunit&semVerLevel=2.0.0&prerelease=true",
+  },
+]
+`;
+
+exports[`datasource/nuget getReleases empty packages list (v3) 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api.nuget.org/v3/index.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api-v2v3search-0.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api-v2v3search-0.nuget.org/query?q=PackageId:nunit&semVerLevel=2.0.0&prerelease=true",
+  },
+]
+`;
+
+exports[`datasource/nuget getReleases extracts feed version from registry URL hash 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "my-registry",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://my-registry/",
+  },
+]
+`;
+
 exports[`datasource/nuget getReleases handles paginated results (v2) 1`] = `
 Object {
   "pkgName": "nunit",
@@ -14,6 +79,29 @@ Object {
 }
 `;
 
+exports[`datasource/nuget getReleases handles paginated results (v2) 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "www.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://www.nuget.org/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "example.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://example.org/",
+  },
+]
+`;
+
 exports[`datasource/nuget getReleases processes real data (v2) 1`] = `
 Object {
   "pkgName": "nunit",
@@ -152,6 +240,20 @@ Object {
 }
 `;
 
+exports[`datasource/nuget getReleases processes real data (v2) 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "www.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://www.nuget.org/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl",
+  },
+]
+`;
+
 exports[`datasource/nuget getReleases processes real data (v3) feed is a nuget.org 1`] = `
 Object {
   "pkgName": "nunit",
@@ -248,6 +350,40 @@ Object {
 }
 `;
 
+exports[`datasource/nuget getReleases processes real data (v3) feed is a nuget.org 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api.nuget.org/v3/index.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api-v2v3search-0.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api-v2v3search-0.nuget.org/query?q=PackageId:nunit&semVerLevel=2.0.0&prerelease=true",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "api.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api.nuget.org/v3-flatcontainer/nunit/3.11.0/nunit.nuspec",
+  },
+]
+`;
+
 exports[`datasource/nuget getReleases processes real data (v3) feed is not a nuget.org 1`] = `
 Object {
   "pkgName": "nunit",
@@ -344,6 +480,70 @@ Object {
 }
 `;
 
+exports[`datasource/nuget getReleases processes real data (v3) feed is not a nuget.org 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "myprivatefeed",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://myprivatefeed/index.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api-v2v3search-0.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api-v2v3search-0.nuget.org/query?q=nunit",
+  },
+]
+`;
+
+exports[`datasource/nuget getReleases processes real data (v3) feed is not a nuget.org with mismatch 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "myprivatefeed",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://myprivatefeed/index.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api-v2v3search-0.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api-v2v3search-0.nuget.org/query?q=nun",
+  },
+]
+`;
+
+exports[`datasource/nuget getReleases processes real data no relase (v2) 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "www.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://www.nuget.org/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl",
+  },
+]
+`;
+
 exports[`datasource/nuget getReleases processes real data with no github project url (v2) 1`] = `
 Object {
   "pkgName": "nunit",
@@ -356,6 +556,20 @@ Object {
 }
 `;
 
+exports[`datasource/nuget getReleases processes real data with no github project url (v2) 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "www.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://www.nuget.org/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl",
+  },
+]
+`;
+
 exports[`datasource/nuget getReleases processes real data with no github project url (v3) 1`] = `
 Object {
   "pkgName": "nunit",
@@ -368,6 +582,31 @@ Object {
 }
 `;
 
+exports[`datasource/nuget getReleases processes real data with no github project url (v3) 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "myprivatefeed",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://myprivatefeed/index.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api-v2v3search-0.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api-v2v3search-0.nuget.org/query?q=nunit",
+  },
+]
+`;
+
 exports[`datasource/nuget getReleases processes real data without project url (v2) 1`] = `
 Object {
   "pkgName": "nunit",
@@ -505,6 +744,20 @@ Object {
 }
 `;
 
+exports[`datasource/nuget getReleases processes real data without project url (v2) 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "www.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://www.nuget.org/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl",
+  },
+]
+`;
+
 exports[`datasource/nuget getReleases processes real data without project url (v3) 1`] = `
 Object {
   "pkgName": "nunit",
@@ -600,6 +853,31 @@ Object {
 }
 `;
 
+exports[`datasource/nuget getReleases processes real data without project url (v3) 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "myprivatefeed",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://myprivatefeed/index.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api-v2v3search-0.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api-v2v3search-0.nuget.org/query?q=nunit",
+  },
+]
+`;
+
 exports[`datasource/nuget getReleases returns deduplicated results 1`] = `
 Object {
   "pkgName": "nunit",
@@ -698,3 +976,241 @@ Object {
   "sourceUrl": "https://github.com/nunit/nunit",
 }
 `;
+
+exports[`datasource/nuget getReleases returns deduplicated results 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api.nuget.org/v3/index.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api-v2v3search-0.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api-v2v3search-0.nuget.org/query?q=PackageId:nunit&semVerLevel=2.0.0&prerelease=true",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "api.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api.nuget.org/v3-flatcontainer/nunit/3.11.0/nunit.nuspec",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "myprivatefeed",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://myprivatefeed/index.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api-v2v3search-0.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api-v2v3search-0.nuget.org/query?q=nunit",
+  },
+]
+`;
+
+exports[`datasource/nuget getReleases returns null for empty result (v2) 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "www.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://www.nuget.org/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl",
+  },
+]
+`;
+
+exports[`datasource/nuget getReleases returns null for empty result (v3) 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api.nuget.org/v3/index.json",
+  },
+]
+`;
+
+exports[`datasource/nuget getReleases returns null for empty result (v3v2) 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api.nuget.org/v3/index.json",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "www.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://www.nuget.org/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl",
+  },
+]
+`;
+
+exports[`datasource/nuget getReleases returns null for non 200 (v2) 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "www.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://www.nuget.org/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl",
+  },
+]
+`;
+
+exports[`datasource/nuget getReleases returns null for non 200 (v3) 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api.nuget.org/v3/index.json",
+  },
+]
+`;
+
+exports[`datasource/nuget getReleases returns null for non 200 (v3v2) 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api.nuget.org/v3/index.json",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "www.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://www.nuget.org/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl",
+  },
+]
+`;
+
+exports[`datasource/nuget getReleases returns null for unknown error (v2) 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "www.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://www.nuget.org/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl",
+  },
+]
+`;
+
+exports[`datasource/nuget getReleases returns null for unknown error (v3v2) 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api.nuget.org/v3/index.json",
+  },
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "www.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://www.nuget.org/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl",
+  },
+]
+`;
+
+exports[`datasource/nuget getReleases returns null for unknown error in getQueryUrlForV3Feed  (v3) 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api.nuget.org/v3/index.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api-v2v3search-0.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api-v2v3search-0.nuget.org/query?q=PackageId:nunit&semVerLevel=2.0.0&prerelease=true",
+  },
+]
+`;
+
+exports[`datasource/nuget getReleases returns null for unknown error in getReleasesFromV3Feed (v3) 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "api.nuget.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://api.nuget.org/v3/index.json",
+  },
+]
+`;
diff --git a/lib/datasource/nuget/index.spec.ts b/lib/datasource/nuget/index.spec.ts
index 8ebf2d1aeec6653d70e800dba684d690bdd92f5c..b39e391e20481baaa5f0229ba38eeabc16013317 100644
--- a/lib/datasource/nuget/index.spec.ts
+++ b/lib/datasource/nuget/index.spec.ts
@@ -1,15 +1,12 @@
 import fs from 'fs';
-import _got from '../../util/got';
+import * as httpMock from '../../../test/httpMock';
 import * as _hostRules from '../../util/host-rules';
 import * as nuget from '.';
 
 const hostRules: any = _hostRules;
 
-jest.mock('../../util/got');
 jest.mock('../../util/host-rules');
 
-const got: any = _got;
-
 const pkgListV3 = fs.readFileSync(
   'lib/datasource/nuget/__fixtures__/nunitV3.json',
   'utf8'
@@ -98,6 +95,11 @@ describe('datasource/nuget', () => {
     beforeEach(() => {
       jest.resetAllMocks();
       hostRules.hosts = jest.fn(() => []);
+      httpMock.setup();
+    });
+
+    afterEach(() => {
+      httpMock.reset();
     });
 
     it(`can't detect nuget feed version`, async () => {
@@ -114,6 +116,7 @@ describe('datasource/nuget', () => {
     });
 
     it('extracts feed version from registry URL hash', async () => {
+      httpMock.scope('https://my-registry').get('/').reply(200);
       const config = {
         lookupName: 'nunit',
         registryUrls: ['https://my-registry#protocolVersion=3'],
@@ -121,319 +124,375 @@ describe('datasource/nuget', () => {
       await nuget.getReleases({
         ...config,
       });
-      expect(got.mock.calls[0][0]).toEqual('https://my-registry/');
+      const trace = httpMock.getTrace();
+      expect(trace[0].url).toEqual('https://my-registry/');
+      expect(trace).toMatchSnapshot();
     });
 
     it(`can't get packages list (v3)`, async () => {
-      got.mockReturnValueOnce({
-        body: JSON.parse(nugetIndexV3),
-        statusCode: 200,
-      });
-      got.mockReturnValueOnce({
-        statusCode: 500,
-      });
+      httpMock
+        .scope('https://api.nuget.org')
+        .get('/v3/index.json')
+        .reply(200, JSON.parse(nugetIndexV3));
+      httpMock
+        .scope('https://api-v2v3search-0.nuget.org')
+        .get('/query?q=PackageId:nunit&semVerLevel=2.0.0&prerelease=true')
+        .reply(500);
+
       const res = await nuget.getReleases({
         ...configV3,
       });
 
       expect(res).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it(`empty packages list (v3)`, async () => {
-      got.mockReturnValueOnce({
-        body: JSON.parse(nugetIndexV3),
-        statusCode: 200,
-      });
-      got.mockReturnValueOnce({
-        body: JSON.parse('{"totalHits": 0}'),
-        statusCode: 200,
-      });
+      httpMock
+        .scope('https://api.nuget.org')
+        .get('/v3/index.json')
+        .reply(200, JSON.parse(nugetIndexV3));
+      httpMock
+        .scope('https://api-v2v3search-0.nuget.org')
+        .get('/query?q=PackageId:nunit&semVerLevel=2.0.0&prerelease=true')
+        .reply(200, JSON.parse('{"totalHits": 0}'));
+
       const res = await nuget.getReleases({
         ...configV3,
       });
 
       expect(res).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('returns null for empty result (v3v2)', async () => {
-      got.mockReturnValueOnce({});
+      httpMock
+        .scope('https://api.nuget.org')
+        .get('/v3/index.json')
+        .reply(200, {});
+      httpMock
+        .scope('https://www.nuget.org')
+        .get(
+          '/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl'
+        )
+        .reply(200, null);
       expect(
         await nuget.getReleases({
           ...configV3V2,
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for empty result (v2)', async () => {
-      got.mockReturnValueOnce({});
+      httpMock
+        .scope('https://www.nuget.org')
+        .get(
+          '/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl'
+        )
+        .reply(200, {});
       expect(
         await nuget.getReleases({
           ...configV2,
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for empty result (v3)', async () => {
-      got.mockReturnValueOnce({});
-      expect(
-        await nuget.getReleases({
-          ...configV3,
-        })
-      ).toBeNull();
+      httpMock
+        .scope('https://api.nuget.org')
+        .get('/v3/index.json')
+        .reply(200, {});
+      const res = await nuget.getReleases({
+        ...configV3,
+      });
+      expect(res).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('returns null for non 200 (v3v2)', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 500,
-        })
-      );
+      httpMock.scope('https://api.nuget.org').get('/v3/index.json').reply(500);
+      httpMock
+        .scope('https://www.nuget.org')
+        .get(
+          '/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl'
+        )
+        .reply(500);
       expect(
         await nuget.getReleases({
           ...configV3V2,
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for non 200 (v3)', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 500,
-        })
-      );
+      httpMock.scope('https://api.nuget.org').get('/v3/index.json').reply(500);
       expect(
         await nuget.getReleases({
           ...configV3,
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for non 200 (v2)', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 500,
-        })
-      );
+      httpMock
+        .scope('https://www.nuget.org')
+        .get(
+          '/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl'
+        )
+        .reply(500);
       expect(
         await nuget.getReleases({
           ...configV2,
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('returns null for unknown error (v3v2)', async () => {
-      got.mockImplementationOnce(() => {
-        throw new Error();
-      });
+      httpMock
+        .scope('https://api.nuget.org')
+        .get('/v3/index.json')
+        .replyWithError('');
+      httpMock
+        .scope('https://www.nuget.org')
+        .get(
+          '/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl'
+        )
+        .replyWithError('');
       expect(
         await nuget.getReleases({
           ...configV3V2,
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns deduplicated results', async () => {
-      got
-        .mockReturnValueOnce({
-          body: JSON.parse(nugetIndexV3),
-          statusCode: 200,
-        })
-        .mockReturnValueOnce({
-          body: JSON.parse(pkgListV3),
-          statusCode: 200,
-        })
-        .mockReturnValueOnce({
-          body: pkgInfoV3FromNuget,
-          statusCode: 200,
-        })
-        .mockReturnValueOnce({
-          body: JSON.parse(nugetIndexV3),
-          statusCode: 200,
-        })
-        .mockReturnValueOnce({
-          body: JSON.parse(pkgListV3PrivateFeed),
-          statusCode: 200,
-        })
-        .mockReturnValueOnce({
-          body: pkgInfoV3FromNuget,
-          statusCode: 200,
-        });
+      httpMock
+        .scope('https://api.nuget.org')
+        .get('/v3/index.json')
+        .reply(200, JSON.parse(nugetIndexV3))
+        .get('/v3-flatcontainer/nunit/3.11.0/nunit.nuspec')
+        .reply(200, pkgInfoV3FromNuget);
+      httpMock
+        .scope('https://api-v2v3search-0.nuget.org')
+        .get('/query?q=PackageId:nunit&semVerLevel=2.0.0&prerelease=true')
+        .reply(200, JSON.parse(pkgListV3))
+        .get('/query?q=nunit')
+        .reply(200, JSON.parse(pkgListV3PrivateFeed));
+      httpMock
+        .scope('https://myprivatefeed')
+        .get('/index.json')
+        .reply(200, JSON.parse(nugetIndexV3));
+
       const res = await nuget.getReleases({
         ...configV3Multiple,
       });
       expect(res).not.toBeNull();
       expect(res).toMatchSnapshot();
       expect(res.releases).toHaveLength(30);
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for unknown error in getReleasesFromV3Feed (v3)', async () => {
-      got.mockImplementationOnce(() => {
-        throw new Error();
-      });
+      httpMock
+        .scope('https://api.nuget.org')
+        .get('/v3/index.json')
+        .replyWithError('');
       expect(
         await nuget.getReleases({
           ...configV3,
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for unknown error in getQueryUrlForV3Feed  (v3)', async () => {
-      got.mockReturnValueOnce({
-        body: JSON.parse(nugetIndexV3),
-        statusCode: 200,
-      });
-      got.mockImplementationOnce(() => {
-        throw new Error();
-      });
+      httpMock
+        .scope('https://api.nuget.org')
+        .get('/v3/index.json')
+        .reply(200, JSON.parse(nugetIndexV3));
+      httpMock
+        .scope('https://api-v2v3search-0.nuget.org')
+        .get('/query?q=PackageId:nunit&semVerLevel=2.0.0&prerelease=true')
+        .replyWithError('');
       expect(
         await nuget.getReleases({
           ...configV3,
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for unknown error (v2)', async () => {
-      got.mockImplementationOnce(() => {
-        throw new Error();
-      });
+      httpMock
+        .scope('https://www.nuget.org')
+        .get(
+          '/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl'
+        )
+        .replyWithError('');
       expect(
         await nuget.getReleases({
           ...configV2,
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real data (v3) feed is a nuget.org', async () => {
-      got.mockReturnValueOnce({
-        body: JSON.parse(nugetIndexV3),
-        statusCode: 200,
-      });
-      got.mockReturnValueOnce({
-        body: JSON.parse(pkgListV3),
-        statusCode: 200,
-      });
-      got.mockReturnValueOnce({
-        body: pkgInfoV3FromNuget,
-        statusCode: 200,
-      });
+      httpMock
+        .scope('https://api.nuget.org')
+        .get('/v3/index.json')
+        .reply(200, JSON.parse(nugetIndexV3))
+        .get('/v3-flatcontainer/nunit/3.11.0/nunit.nuspec')
+        .reply(200, pkgInfoV3FromNuget);
+      httpMock
+        .scope('https://api-v2v3search-0.nuget.org')
+        .get('/query?q=PackageId:nunit&semVerLevel=2.0.0&prerelease=true')
+        .reply(200, JSON.parse(pkgListV3));
       const res = await nuget.getReleases({
         ...configV3,
       });
       expect(res).not.toBeNull();
       expect(res).toMatchSnapshot();
       expect(res.sourceUrl).toBeDefined();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real data (v3) feed is not a nuget.org', async () => {
-      got.mockReturnValueOnce({
-        body: JSON.parse(nugetIndexV3),
-        statusCode: 200,
-      });
-      got.mockReturnValueOnce({
-        body: JSON.parse(pkgListV3),
-        statusCode: 200,
-      });
+      httpMock
+        .scope('https://api-v2v3search-0.nuget.org')
+        .get('/query?q=nunit')
+        .reply(200, JSON.parse(pkgListV3));
+      httpMock
+        .scope('https://myprivatefeed')
+        .get('/index.json')
+        .reply(200, JSON.parse(nugetIndexV3));
+
       const res = await nuget.getReleases({
         ...configV3NotNugetOrg,
       });
       expect(res).not.toBeNull();
       expect(res).toMatchSnapshot();
       expect(res.sourceUrl).toBeDefined();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real data (v3) feed is not a nuget.org with mismatch', async () => {
-      got.mockReturnValueOnce({
-        body: JSON.parse(nugetIndexV3),
-        statusCode: 200,
-      });
-      got.mockReturnValueOnce({
-        body: JSON.parse(pkgListV3),
-        statusCode: 200,
-      });
+      httpMock
+        .scope('https://api-v2v3search-0.nuget.org')
+        .get('/query?q=nun')
+        .reply(200, JSON.parse(pkgListV3));
+      httpMock
+        .scope('https://myprivatefeed')
+        .get('/index.json')
+        .reply(200, JSON.parse(nugetIndexV3));
       const res = await nuget.getReleases({
         ...configV3NotNugetOrg,
         lookupName: 'nun',
       });
       expect(res).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real data without project url (v3)', async () => {
-      got.mockReturnValueOnce({
-        body: JSON.parse(nugetIndexV3),
-        statusCode: 200,
-      });
-      got.mockReturnValueOnce({
-        body: JSON.parse(pkgListV3WithoutProkjectUrl),
-        statusCode: 200,
-      });
+      httpMock
+        .scope('https://api-v2v3search-0.nuget.org')
+        .get('/query?q=nunit')
+        .reply(200, JSON.parse(pkgListV3WithoutProkjectUrl));
+      httpMock
+        .scope('https://myprivatefeed')
+        .get('/index.json')
+        .reply(200, JSON.parse(nugetIndexV3));
       const res = await nuget.getReleases({
         ...configV3NotNugetOrg,
       });
       expect(res).not.toBeNull();
       expect(res).toMatchSnapshot();
       expect(res.sourceUrl).not.toBeDefined();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real data with no github project url (v3)', async () => {
-      got.mockReturnValueOnce({
-        body: JSON.parse(nugetIndexV3),
-        statusCode: 200,
-      });
-      got.mockReturnValueOnce({
-        body: JSON.parse(pkgListV3NoGitHubProjectUrl),
-        statusCode: 200,
-      });
+      httpMock
+        .scope('https://api-v2v3search-0.nuget.org')
+        .get('/query?q=nunit')
+        .reply(200, JSON.parse(pkgListV3NoGitHubProjectUrl));
+      httpMock
+        .scope('https://myprivatefeed')
+        .get('/index.json')
+        .reply(200, JSON.parse(nugetIndexV3));
       const res = await nuget.getReleases({
         ...configV3NotNugetOrg,
       });
       expect(res).not.toBeNull();
       expect(res).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real data (v2)', async () => {
-      got.mockReturnValueOnce({
-        body: pkgListV2,
-        statusCode: 200,
-      });
+      httpMock
+        .scope('https://www.nuget.org')
+        .get(
+          '/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl'
+        )
+        .reply(200, pkgListV2);
       const res = await nuget.getReleases({
         ...configV2,
       });
       expect(res).not.toBeNull();
       expect(res).toMatchSnapshot();
       expect(res.sourceUrl).toBeDefined();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real data no relase (v2)', async () => {
-      got.mockReturnValueOnce({
-        body: pkgListV2NoRelease,
-        statusCode: 200,
-      });
+      httpMock
+        .scope('https://www.nuget.org')
+        .get(
+          '/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl'
+        )
+        .reply(200, pkgListV2NoRelease);
       const res = await nuget.getReleases({
         ...configV2,
       });
       expect(res).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real data without project url (v2)', async () => {
-      got.mockReturnValueOnce({
-        body: pkgListV2WithoutProjectUrl,
-        statusCode: 200,
-      });
+      httpMock
+        .scope('https://www.nuget.org')
+        .get(
+          '/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl'
+        )
+        .reply(200, pkgListV2WithoutProjectUrl);
       const res = await nuget.getReleases({
         ...configV2,
       });
       expect(res).not.toBeNull();
       expect(res).toMatchSnapshot();
       expect(res.sourceUrl).not.toBeDefined();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real data with no github project url (v2)', async () => {
-      got.mockReturnValueOnce({
-        body: pkgListV2NoGitHubProjectUrl,
-        statusCode: 200,
-      });
+      httpMock
+        .scope('https://www.nuget.org')
+        .get(
+          '/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl'
+        )
+        .reply(200, pkgListV2NoGitHubProjectUrl);
       const res = await nuget.getReleases({
         ...configV2,
       });
       expect(res).not.toBeNull();
       expect(res).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('handles paginated results (v2)', async () => {
-      got.mockReturnValueOnce({
-        body: pkgListV2Page1of2,
-        statusCode: 200,
-      });
-      got.mockReturnValueOnce({
-        body: pkgListV2Page2of2,
-        statusCode: 200,
-      });
+      httpMock
+        .scope('https://www.nuget.org')
+        .get(
+          '/api/v2//FindPackagesById()?id=%27nunit%27&$select=Version,IsLatestVersion,ProjectUrl'
+        )
+        .reply(200, pkgListV2Page1of2);
+      httpMock
+        .scope('https://example.org')
+        .get('/')
+        .reply(200, pkgListV2Page2of2);
       const res = await nuget.getReleases({
         ...configV2,
       });
       expect(res).not.toBeNull();
       expect(res).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
   });
 });
diff --git a/lib/datasource/orb/__snapshots__/index.spec.ts.snap b/lib/datasource/orb/__snapshots__/index.spec.ts.snap
index 7451fb7e7d0fae06dc5217c3a32243e33d6d39c4..fd44a5442d86385363d422612d136ac699264ef6 100644
--- a/lib/datasource/orb/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/orb/__snapshots__/index.spec.ts.snap
@@ -50,6 +50,38 @@ Object {
 }
 `;
 
+exports[`datasource/orb getReleases processes homeUrl 2`] = `
+Array [
+  Object {
+    "graphql": Object {
+      "query": Object {
+        "orb": Object {
+          "__args": Object {
+            "name": "hyper-expanse/library-release-workflows",
+          },
+          "homeUrl": null,
+          "name": null,
+          "versions": Object {
+            "createdAt": null,
+            "version": null,
+          },
+        },
+      },
+    },
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "content-length": 128,
+      "content-type": "application/json",
+      "host": "circleci.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "POST",
+    "url": "https://circleci.com/graphql-unstable",
+  },
+]
+`;
+
 exports[`datasource/orb getReleases processes real data 1`] = `
 Object {
   "homepage": "https://circleci.com/orbs/registry/orb/hyper-expanse/library-release-workflows",
@@ -99,3 +131,163 @@ Object {
   "versions": Object {},
 }
 `;
+
+exports[`datasource/orb getReleases processes real data 2`] = `
+Array [
+  Object {
+    "graphql": Object {
+      "query": Object {
+        "orb": Object {
+          "__args": Object {
+            "name": "hyper-expanse/library-release-workflows",
+          },
+          "homeUrl": null,
+          "name": null,
+          "versions": Object {
+            "createdAt": null,
+            "version": null,
+          },
+        },
+      },
+    },
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "content-length": 128,
+      "content-type": "application/json",
+      "host": "circleci.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "POST",
+    "url": "https://circleci.com/graphql-unstable",
+  },
+]
+`;
+
+exports[`datasource/orb getReleases returns null for 404 1`] = `
+Array [
+  Object {
+    "graphql": Object {
+      "query": Object {
+        "orb": Object {
+          "__args": Object {
+            "name": "hyper-expanse/library-release-workflows",
+          },
+          "homeUrl": null,
+          "name": null,
+          "versions": Object {
+            "createdAt": null,
+            "version": null,
+          },
+        },
+      },
+    },
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "content-length": 128,
+      "content-type": "application/json",
+      "host": "circleci.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "POST",
+    "url": "https://circleci.com/graphql-unstable",
+  },
+]
+`;
+
+exports[`datasource/orb getReleases returns null for empty result 1`] = `
+Array [
+  Object {
+    "graphql": Object {
+      "query": Object {
+        "orb": Object {
+          "__args": Object {
+            "name": "hyper-expanse/library-release-workflows",
+          },
+          "homeUrl": null,
+          "name": null,
+          "versions": Object {
+            "createdAt": null,
+            "version": null,
+          },
+        },
+      },
+    },
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "content-length": 128,
+      "content-type": "application/json",
+      "host": "circleci.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "POST",
+    "url": "https://circleci.com/graphql-unstable",
+  },
+]
+`;
+
+exports[`datasource/orb getReleases returns null for missing orb 1`] = `
+Array [
+  Object {
+    "graphql": Object {
+      "query": Object {
+        "orb": Object {
+          "__args": Object {
+            "name": "hyper-expanse/library-release-wonkflows",
+          },
+          "homeUrl": null,
+          "name": null,
+          "versions": Object {
+            "createdAt": null,
+            "version": null,
+          },
+        },
+      },
+    },
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "content-length": 128,
+      "content-type": "application/json",
+      "host": "circleci.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "POST",
+    "url": "https://circleci.com/graphql-unstable",
+  },
+]
+`;
+
+exports[`datasource/orb getReleases returns null for unknown error 1`] = `
+Array [
+  Object {
+    "graphql": Object {
+      "query": Object {
+        "orb": Object {
+          "__args": Object {
+            "name": "hyper-expanse/library-release-workflows",
+          },
+          "homeUrl": null,
+          "name": null,
+          "versions": Object {
+            "createdAt": null,
+            "version": null,
+          },
+        },
+      },
+    },
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "content-length": 128,
+      "content-type": "application/json",
+      "host": "circleci.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "POST",
+    "url": "https://circleci.com/graphql-unstable",
+  },
+]
+`;
diff --git a/lib/datasource/orb/index.spec.ts b/lib/datasource/orb/index.spec.ts
index c4ff5447da465e9372c0921134de040358c1de56..7e173785f1bbb8074ef3cfd0299482bcca5120ff 100644
--- a/lib/datasource/orb/index.spec.ts
+++ b/lib/datasource/orb/index.spec.ts
@@ -1,10 +1,6 @@
-import _got from '../../util/got';
+import * as httpMock from '../../../test/httpMock';
 import * as datasource from '.';
 
-jest.mock('../../util/got');
-
-const got: any = _got;
-
 const orbData = {
   data: {
     orb: {
@@ -26,69 +22,76 @@ const orbData = {
   },
 };
 
+const baseUrl = 'https://circleci.com';
+
 describe('datasource/orb', () => {
   describe('getReleases', () => {
     beforeEach(() => {
       jest.clearAllMocks();
+      httpMock.setup();
+    });
+
+    afterEach(() => {
+      httpMock.reset();
     });
+
     it('returns null for empty result', async () => {
-      got.mockReturnValueOnce({ body: {} });
+      httpMock.scope(baseUrl).post('/graphql-unstable').reply(200, {});
       expect(
         await datasource.getReleases({
           lookupName: 'hyper-expanse/library-release-workflows',
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for missing orb', async () => {
-      got.mockReturnValueOnce({ body: { data: {} } });
+      httpMock
+        .scope(baseUrl)
+        .post('/graphql-unstable')
+        .reply(200, { data: {} });
       expect(
         await datasource.getReleases({
           lookupName: 'hyper-expanse/library-release-wonkflows',
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for 404', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 404,
-        })
-      );
+      httpMock.scope(baseUrl).post('/graphql-unstable').reply(404);
       expect(
         await datasource.getReleases({
           lookupName: 'hyper-expanse/library-release-workflows',
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for unknown error', async () => {
-      got.mockImplementationOnce(() => {
-        throw new Error();
-      });
+      httpMock.scope(baseUrl).post('/graphql-unstable').replyWithError('');
       expect(
         await datasource.getReleases({
           lookupName: 'hyper-expanse/library-release-workflows',
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real data', async () => {
-      got.mockReturnValueOnce({
-        body: orbData,
-      });
+      httpMock.scope(baseUrl).post('/graphql-unstable').reply(200, orbData);
       const res = await datasource.getReleases({
         lookupName: 'hyper-expanse/library-release-workflows',
       });
       expect(res).toMatchSnapshot();
       expect(res).not.toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes homeUrl', async () => {
       orbData.data.orb.homeUrl = 'https://google.com';
-      got.mockReturnValueOnce({
-        body: orbData,
-      });
+      httpMock.scope(baseUrl).post('/graphql-unstable').reply(200, orbData);
       const res = await datasource.getReleases({
         lookupName: 'hyper-expanse/library-release-workflows',
       });
       expect(res).toMatchSnapshot();
       expect(res.homepage).toEqual('https://google.com');
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
   });
 });
diff --git a/lib/datasource/packagist/__snapshots__/index.spec.ts.snap b/lib/datasource/packagist/__snapshots__/index.spec.ts.snap
index fa9942fe84446c6489eb82f35e2b7dca97d482c2..fd5e7a8af7b5f0874e87716867b336e136067c4b 100644
--- a/lib/datasource/packagist/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/packagist/__snapshots__/index.spec.ts.snap
@@ -100,6 +100,156 @@ Object {
 }
 `;
 
+exports[`datasource/packagist getReleases adds packagist source implicitly 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "packagist.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://packagist.org/p/drewm/mailchimp-api.json",
+  },
+]
+`;
+
+exports[`datasource/packagist getReleases handles auth rejections 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "composer.renovatebot.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://composer.renovatebot.com/packages.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "packagist.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://packagist.org/p/vendor/package-name.json",
+  },
+]
+`;
+
+exports[`datasource/packagist getReleases handles not found registries 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "composer.renovatebot.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://composer.renovatebot.com/packages.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "packagist.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://packagist.org/p/drewm/mailchip-api.json",
+  },
+]
+`;
+
+exports[`datasource/packagist getReleases handles provider-includes miss 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "composer.renovatebot.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://composer.renovatebot.com/packages.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "composer.renovatebot.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://composer.renovatebot.com/p/providers-2018-09$14346045d7a7261cb3a12a6b7a1a7c4151982530347b115e5e277d879cad1942.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "packagist.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://packagist.org/p/some/other.json",
+  },
+]
+`;
+
+exports[`datasource/packagist getReleases handles providers miss 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "composer.renovatebot.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://composer.renovatebot.com/packages.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "packagist.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://packagist.org/p/some/other.json",
+  },
+]
+`;
+
+exports[`datasource/packagist getReleases handles timeouts 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "composer.renovatebot.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://composer.renovatebot.com/packages.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "packagist.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://packagist.org/p/vendor/package-name2.json",
+  },
+]
+`;
+
 exports[`datasource/packagist getReleases processes real versioned data 1`] = `
 Object {
   "homepage": "https://github.com/drewm/mailchimp-api",
@@ -200,6 +350,21 @@ Object {
 }
 `;
 
+exports[`datasource/packagist getReleases processes real versioned data 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "packagist.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://packagist.org/p/drewm/mailchimp-api.json",
+  },
+]
+`;
+
 exports[`datasource/packagist getReleases supports includes packages 1`] = `
 Object {
   "homepage": "http://guzzlephp.org/",
@@ -345,6 +510,33 @@ Object {
 }
 `;
 
+exports[`datasource/packagist getReleases supports includes packages 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk",
+      "host": "composer.renovatebot.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://composer.renovatebot.com/packages.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "authorization": "Basic c29tZS11c2VybmFtZTpzb21lLXBhc3N3b3Jk",
+      "host": "composer.renovatebot.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://composer.renovatebot.com/include/all$afbf74d51f31c7cbb5ff10304f9290bfb4f4e68b.json",
+  },
+]
+`;
+
 exports[`datasource/packagist getReleases supports plain packages 1`] = `
 Object {
   "homepage": undefined,
@@ -374,6 +566,21 @@ Object {
 }
 `;
 
+exports[`datasource/packagist getReleases supports plain packages 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "composer.renovatebot.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://composer.renovatebot.com/packages.json",
+  },
+]
+`;
+
 exports[`datasource/packagist getReleases supports provider-includes 1`] = `
 Object {
   "homepage": "https://wordpress.org/plugins/1beyt/",
@@ -414,6 +621,41 @@ Object {
 }
 `;
 
+exports[`datasource/packagist getReleases supports provider-includes 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "composer.renovatebot.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://composer.renovatebot.com/packages.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "composer.renovatebot.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://composer.renovatebot.com/p/providers-2018-09$14346045d7a7261cb3a12a6b7a1a7c4151982530347b115e5e277d879cad1942.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "composer.renovatebot.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://composer.renovatebot.com/p/wpackagist-plugin/1beyt$b574a802b5bf20a58c0f027e73aea2a75d23a6f654afc298a8dc467331be316a.json",
+  },
+]
+`;
+
 exports[`datasource/packagist getReleases supports providers 1`] = `
 Object {
   "homepage": "https://wordpress.org/plugins/1beyt/",
@@ -453,3 +695,28 @@ Object {
   "sourceUrl": "https://plugins.svn.wordpress.org/1beyt/",
 }
 `;
+
+exports[`datasource/packagist getReleases supports providers 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "composer.renovatebot.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://composer.renovatebot.com/packages.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "composer.renovatebot.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://composer.renovatebot.com/p/wpackagist-plugin/1beyt$b574a802b5bf20a58c0f027e73aea2a75d23a6f654afc298a8dc467331be316a.json",
+  },
+]
+`;
diff --git a/lib/datasource/packagist/index.spec.ts b/lib/datasource/packagist/index.spec.ts
index 3fedf59cf19253f06bb69de954791200d47588c2..a1cef7d2c1194f6de1032b51b51840080a56113b 100644
--- a/lib/datasource/packagist/index.spec.ts
+++ b/lib/datasource/packagist/index.spec.ts
@@ -1,13 +1,11 @@
 import fs from 'fs';
-import _got from '../../util/got';
+import * as httpMock from '../../../test/httpMock';
 import * as _hostRules from '../../util/host-rules';
 import * as composerVersioning from '../../versioning/composer';
 import * as packagist from '.';
 
-jest.mock('../../util/got');
 jest.mock('../../util/host-rules');
 
-const got: any = _got;
 const hostRules = _hostRules;
 
 const includesJson: any = fs.readFileSync(
@@ -20,11 +18,14 @@ const mailchimpJson: any = fs.readFileSync(
   'lib/datasource/packagist/__fixtures__/mailchimp-api.json'
 );
 
+const baseUrl = 'https://packagist.org';
+
 describe('datasource/packagist', () => {
   describe('getReleases', () => {
     let config: any;
     beforeEach(() => {
       jest.resetAllMocks();
+      httpMock.setup();
       hostRules.find = jest.fn((input) => input);
       hostRules.hosts = jest.fn(() => []);
       config = {
@@ -35,6 +36,11 @@ describe('datasource/packagist', () => {
         ],
       };
     });
+
+    afterEach(() => {
+      httpMock.reset();
+    });
+
     it('supports custom registries', async () => {
       config = {
         registryUrls: ['https://composer.renovatebot.com'],
@@ -56,51 +62,55 @@ describe('datasource/packagist', () => {
           },
         },
       };
-      got.mockReturnValueOnce({
-        body: packagesOnly,
-      });
+      httpMock
+        .scope('https://composer.renovatebot.com')
+        .get('/packages.json')
+        .reply(200, packagesOnly);
       const res = await packagist.getReleases({
         ...config,
         lookupName: 'vendor/package-name',
       });
       expect(res).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('handles timeouts', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          code: 'ETIMEDOUT',
-        })
-      );
+      httpMock
+        .scope('https://composer.renovatebot.com')
+        .get('/packages.json')
+        .replyWithError({ code: 'ETIMEDOUT' });
+      httpMock.scope(baseUrl).get('/p/vendor/package-name2.json').reply(200);
       const res = await packagist.getReleases({
         ...config,
         lookupName: 'vendor/package-name2',
       });
       expect(res).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('handles auth rejections', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 403,
-        })
-      );
+      httpMock
+        .scope('https://composer.renovatebot.com')
+        .get('/packages.json')
+        .reply(403);
+      httpMock.scope(baseUrl).get('/p/vendor/package-name.json').reply(200);
       const res = await packagist.getReleases({
         ...config,
         lookupName: 'vendor/package-name',
       });
       expect(res).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('handles not found registries', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 404,
-          url: 'https://some.registry/packages.json',
-        })
-      );
+      httpMock
+        .scope('https://composer.renovatebot.com')
+        .get('/packages.json')
+        .reply(404);
+      httpMock.scope(baseUrl).get('/p/drewm/mailchip-api.json').reply(200);
       const res = await packagist.getReleases({
         ...config,
         lookupName: 'drewm/mailchip-api',
       });
       expect(res).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('supports includes packages', async () => {
       hostRules.find = jest.fn(() => ({
@@ -115,18 +125,19 @@ describe('datasource/packagist', () => {
           },
         },
       };
-      got.mockReturnValueOnce({
-        body: packagesJson,
-      });
-      got.mockReturnValueOnce({
-        body: JSON.parse(includesJson),
-      });
+      httpMock
+        .scope('https://composer.renovatebot.com')
+        .get('/packages.json')
+        .reply(200, packagesJson)
+        .get('/include/all$afbf74d51f31c7cbb5ff10304f9290bfb4f4e68b.json')
+        .reply(200, JSON.parse(includesJson));
       const res = await packagist.getReleases({
         ...config,
         lookupName: 'guzzlehttp/guzzle',
       });
       expect(res).toMatchSnapshot();
       expect(res).not.toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('supports provider-includes', async () => {
       const packagesJson = {
@@ -139,9 +150,6 @@ describe('datasource/packagist', () => {
           },
         },
       };
-      got.mockReturnValueOnce({
-        body: packagesJson,
-      });
       const fileJson = {
         providers: {
           'wpackagist-plugin/1337-rss-feed-made-for-sharing': {
@@ -154,18 +162,25 @@ describe('datasource/packagist', () => {
           },
         },
       };
-      got.mockReturnValueOnce({
-        body: fileJson,
-      });
-      got.mockReturnValueOnce({
-        body: JSON.parse(beytJson),
-      });
+      httpMock
+        .scope('https://composer.renovatebot.com')
+        .get('/packages.json')
+        .reply(200, packagesJson)
+        .get(
+          '/p/providers-2018-09$14346045d7a7261cb3a12a6b7a1a7c4151982530347b115e5e277d879cad1942.json'
+        )
+        .reply(200, fileJson)
+        .get(
+          '/p/wpackagist-plugin/1beyt$b574a802b5bf20a58c0f027e73aea2a75d23a6f654afc298a8dc467331be316a.json'
+        )
+        .reply(200, JSON.parse(beytJson));
       const res = await packagist.getReleases({
         ...config,
         lookupName: 'wpackagist-plugin/1beyt',
       });
       expect(res).toMatchSnapshot();
       expect(res).not.toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('handles provider-includes miss', async () => {
       const packagesJson = {
@@ -178,9 +193,6 @@ describe('datasource/packagist', () => {
           },
         },
       };
-      got.mockReturnValueOnce({
-        body: packagesJson,
-      });
       const fileJson = {
         providers: {
           'wpackagist-plugin/1337-rss-feed-made-for-sharing': {
@@ -193,17 +205,24 @@ describe('datasource/packagist', () => {
           },
         },
       };
-      got.mockReturnValueOnce({
-        body: fileJson,
-      });
-      got.mockReturnValueOnce({
-        body: JSON.parse(beytJson),
-      });
+      httpMock
+        .scope('https://composer.renovatebot.com')
+        .get('/packages.json')
+        .reply(200, packagesJson)
+        .get(
+          '/p/providers-2018-09$14346045d7a7261cb3a12a6b7a1a7c4151982530347b115e5e277d879cad1942.json'
+        )
+        .reply(200, fileJson);
+      httpMock
+        .scope(baseUrl)
+        .get('/p/some/other.json')
+        .reply(200, JSON.parse(beytJson));
       const res = await packagist.getReleases({
         ...config,
         lookupName: 'some/other',
       });
       expect(res).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('supports providers', async () => {
       const packagesJson = {
@@ -220,18 +239,21 @@ describe('datasource/packagist', () => {
           },
         },
       };
-      got.mockReturnValueOnce({
-        body: packagesJson,
-      });
-      got.mockReturnValueOnce({
-        body: JSON.parse(beytJson),
-      });
+      httpMock
+        .scope('https://composer.renovatebot.com')
+        .get('/packages.json')
+        .reply(200, packagesJson)
+        .get(
+          '/p/wpackagist-plugin/1beyt$b574a802b5bf20a58c0f027e73aea2a75d23a6f654afc298a8dc467331be316a.json'
+        )
+        .reply(200, JSON.parse(beytJson));
       const res = await packagist.getReleases({
         ...config,
         lookupName: 'wpackagist-plugin/1beyt',
       });
       expect(res).toMatchSnapshot();
       expect(res).not.toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('handles providers miss', async () => {
       const packagesJson = {
@@ -248,22 +270,26 @@ describe('datasource/packagist', () => {
           },
         },
       };
-      got.mockReturnValueOnce({
-        body: packagesJson,
-      });
-      got.mockReturnValueOnce({
-        body: JSON.parse(beytJson),
-      });
+      httpMock
+        .scope('https://composer.renovatebot.com')
+        .get('/packages.json')
+        .reply(200, packagesJson);
+      httpMock
+        .scope(baseUrl)
+        .get('/p/some/other.json')
+        .reply(200, JSON.parse(beytJson));
       const res = await packagist.getReleases({
         ...config,
         lookupName: 'some/other',
       });
       expect(res).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real versioned data', async () => {
-      got.mockReturnValueOnce({
-        body: JSON.parse(mailchimpJson),
-      });
+      httpMock
+        .scope(baseUrl)
+        .get('/p/drewm/mailchimp-api.json')
+        .reply(200, JSON.parse(mailchimpJson));
       config.registryUrls = ['https://packagist.org'];
       expect(
         await packagist.getReleases({
@@ -271,11 +297,13 @@ describe('datasource/packagist', () => {
           lookupName: 'drewm/mailchimp-api',
         })
       ).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('adds packagist source implicitly', async () => {
-      got.mockReturnValueOnce({
-        body: JSON.parse(mailchimpJson),
-      });
+      httpMock
+        .scope(baseUrl)
+        .get('/p/drewm/mailchimp-api.json')
+        .reply(200, JSON.parse(mailchimpJson));
       config.registryUrls = [];
       expect(
         await packagist.getReleases({
@@ -283,6 +311,7 @@ describe('datasource/packagist', () => {
           lookupName: 'drewm/mailchimp-api',
         })
       ).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
   });
 });
diff --git a/lib/datasource/ruby-version/__snapshots__/index.spec.ts.snap b/lib/datasource/ruby-version/__snapshots__/index.spec.ts.snap
index bb2ef169b30c173d14a36524e1b5bba5fb43cbb9..4ee2d66c0cf8e237383fc9965a71b5a3566d269f 100644
--- a/lib/datasource/ruby-version/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/ruby-version/__snapshots__/index.spec.ts.snap
@@ -673,3 +673,45 @@ Object {
   "sourceUrl": "https://github.com/ruby/ruby",
 }
 `;
+
+exports[`datasource/gradle getReleases parses real data 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "www.ruby-lang.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://www.ruby-lang.org/en/downloads/releases/",
+  },
+]
+`;
+
+exports[`datasource/gradle getReleases throws for 404 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "www.ruby-lang.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://www.ruby-lang.org/en/downloads/releases/",
+  },
+]
+`;
+
+exports[`datasource/gradle getReleases throws for empty result 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "gzip, deflate",
+      "host": "www.ruby-lang.org",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://www.ruby-lang.org/en/downloads/releases/",
+  },
+]
+`;
diff --git a/lib/datasource/ruby-version/index.spec.ts b/lib/datasource/ruby-version/index.spec.ts
index d4bc65e185010f77a017802ae8989b5dd3615d43..5d69dfc9e5cbed98ea68e7e7a9a2e4df3da26166 100644
--- a/lib/datasource/ruby-version/index.spec.ts
+++ b/lib/datasource/ruby-version/index.spec.ts
@@ -1,11 +1,7 @@
 import fs from 'fs';
-import _got from '../../util/got';
+import * as httpMock from '../../../test/httpMock';
 import { getReleases } from '.';
 
-jest.mock('../../util/got');
-
-const got: any = _got;
-
 const rubyReleasesHtml = fs.readFileSync(
   'lib/datasource/ruby-version/__fixtures__/releases.html',
   'utf8'
@@ -13,25 +9,39 @@ const rubyReleasesHtml = fs.readFileSync(
 
 describe('datasource/gradle', () => {
   describe('getReleases', () => {
+    beforeEach(() => {
+      httpMock.setup();
+    });
+
+    afterEach(() => {
+      httpMock.reset();
+    });
+
     it('parses real data', async () => {
-      got.mockReturnValueOnce({
-        body: rubyReleasesHtml,
-      });
+      httpMock
+        .scope('https://www.ruby-lang.org')
+        .get('/en/downloads/releases/')
+        .reply(200, rubyReleasesHtml);
       const res = await getReleases();
       expect(res).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('throws for empty result', async () => {
-      got.mockReturnValueOnce({ body: {} });
+      httpMock
+        .scope('https://www.ruby-lang.org')
+        .get('/en/downloads/releases/')
+        .reply(200, {});
       await expect(getReleases()).rejects.toThrow();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('throws for 404', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 404,
-        })
-      );
+      httpMock
+        .scope('https://www.ruby-lang.org')
+        .get('/en/downloads/releases/')
+        .reply(404);
       await expect(getReleases()).rejects.toThrow();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
   });
 });
diff --git a/lib/datasource/rubygems/__snapshots__/index.spec.ts.snap b/lib/datasource/rubygems/__snapshots__/index.spec.ts.snap
index 3b5d1d791fe1c01b80ca655049d3750e0b9723cb..6a0bf0c46c1744c35723200291f3d0fc2fa6249e 100644
--- a/lib/datasource/rubygems/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/rubygems/__snapshots__/index.spec.ts.snap
@@ -14,6 +14,90 @@ Object {
 }
 `;
 
+exports[`datasource/rubygems getReleases returns a dep for rubygems.org package hit 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "identity",
+      "host": "rubygems.org",
+      "range": "bytes=0-",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://rubygems.org/versions",
+  },
+]
+`;
+
+exports[`datasource/rubygems getReleases returns null for missing pkg 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "thirdparty.com",
+      "hosttype": "rubygems",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://thirdparty.com/api/v1/gems/rails.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "firstparty.com",
+      "hosttype": "rubygems",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://firstparty.com/api/v1/gems/rails.json",
+  },
+]
+`;
+
+exports[`datasource/rubygems getReleases returns null for rubygems.org package miss 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "identity",
+      "host": "rubygems.org",
+      "range": "bytes=0-",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://rubygems.org/versions",
+  },
+]
+`;
+
+exports[`datasource/rubygems getReleases returns null if mismatched name 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "thirdparty.com",
+      "hosttype": "rubygems",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://thirdparty.com/api/v1/gems/rails.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "firstparty.com",
+      "hosttype": "rubygems",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://firstparty.com/api/v1/gems/rails.json",
+  },
+]
+`;
+
 exports[`datasource/rubygems getReleases uses multiple source urls 1`] = `
 Object {
   "homepage": "http://rubyonrails.org",
@@ -2396,6 +2480,44 @@ Object {
 }
 `;
 
+exports[`datasource/rubygems getReleases uses multiple source urls 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "thirdparty.com",
+      "hosttype": "rubygems",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://thirdparty.com/api/v1/gems/rails.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "firstparty.com",
+      "hosttype": "rubygems",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://firstparty.com/api/v1/gems/rails.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "firstparty.com",
+      "hosttype": "rubygems",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://firstparty.com/api/v1/versions/rails.json",
+  },
+]
+`;
+
 exports[`datasource/rubygems getReleases uses rubygems.org if no registry urls were provided 1`] = `
 Object {
   "name": "1pass",
@@ -2410,6 +2532,21 @@ Object {
 }
 `;
 
+exports[`datasource/rubygems getReleases uses rubygems.org if no registry urls were provided 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept-encoding": "identity",
+      "host": "rubygems.org",
+      "range": "bytes=0-",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://rubygems.org/versions",
+  },
+]
+`;
+
 exports[`datasource/rubygems getReleases works with real data 1`] = `
 Object {
   "homepage": "http://rubyonrails.org",
@@ -4791,3 +4928,30 @@ Object {
   "sourceUrl": "https://github.com/rails/rails",
 }
 `;
+
+exports[`datasource/rubygems getReleases works with real data 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "thirdparty.com",
+      "hosttype": "rubygems",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://thirdparty.com/api/v1/gems/rails.json",
+  },
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "thirdparty.com",
+      "hosttype": "rubygems",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://thirdparty.com/api/v1/versions/rails.json",
+  },
+]
+`;
diff --git a/lib/datasource/rubygems/get-rubygems-org.ts b/lib/datasource/rubygems/get-rubygems-org.ts
index 1ac4f4088d9424ead43338573168ed26b0c7e2a8..3eb01a4ed97a8826576338723d474925fcc8156e 100644
--- a/lib/datasource/rubygems/get-rubygems-org.ts
+++ b/lib/datasource/rubygems/get-rubygems-org.ts
@@ -9,6 +9,13 @@ let lastSync = new Date('2000-01-01');
 let packageReleases: Record<string, string[]> = Object.create(null); // Because we might need a "constructor" key
 let contentLength = 0;
 
+// Note: use only for tests
+export function resetCache(): void {
+  lastSync = new Date('2000-01-01');
+  packageReleases = Object.create(null);
+  contentLength = 0;
+}
+
 /* https://bugs.chromium.org/p/v8/issues/detail?id=2869 */
 const copystr = (x: string): string => (' ' + x).slice(1);
 
diff --git a/lib/datasource/rubygems/index.spec.ts b/lib/datasource/rubygems/index.spec.ts
index 151b3acd10f7074031d44ca2516a6f2297d8a930..593ac0d4eb90f0d1338385d8843b1344373d4cd4 100644
--- a/lib/datasource/rubygems/index.spec.ts
+++ b/lib/datasource/rubygems/index.spec.ts
@@ -1,12 +1,11 @@
 import { getPkgReleases } from '..';
-import _got from '../../util/got';
+import * as httpMock from '../../../test/httpMock';
 import * as rubyVersioning from '../../versioning/ruby';
 import railsInfo from './__fixtures__/rails/info.json';
 import railsVersions from './__fixtures__/rails/versions.json';
+import { resetCache } from './get-rubygems-org';
 import * as rubygems from '.';
 
-const got: any = _got;
-
 const rubygemsOrgVersions = `created_at: 2017-03-27T04:38:13+00:00
 ---
 - 1 05d0116933ba44b0b5d0ee19bfd35ccc
@@ -26,8 +25,6 @@ const rubygemsOrgVersions = `created_at: 2017-03-27T04:38:13+00:00
 1pass -0.1.2 abcdef
 21-day-challenge-countdown 0.1.0,0.1.1,0.1.2 57e8873fe713063f4e54e85bbbd709bb`;
 
-jest.mock('../../util/got');
-
 describe('datasource/rubygems', () => {
   describe('getReleases', () => {
     const SKIP_CACHE = process.env.RENOVATE_SKIP_CACHE;
@@ -40,24 +37,40 @@ describe('datasource/rubygems', () => {
     };
 
     beforeEach(() => {
+      resetCache();
+      httpMock.setup();
       process.env.RENOVATE_SKIP_CACHE = 'true';
       jest.resetAllMocks();
     });
 
     afterEach(() => {
+      httpMock.reset();
       process.env.RENOVATE_SKIP_CACHE = SKIP_CACHE;
     });
 
     it('returns null for missing pkg', async () => {
-      got.mockReturnValueOnce({});
+      httpMock
+        .scope('https://firstparty.com')
+        .get('/api/v1/gems/rails.json')
+        .reply(200, null);
+      httpMock
+        .scope('https://thirdparty.com')
+        .get('/api/v1/gems/rails.json')
+        .reply(200, null);
       expect(await getPkgReleases(params)).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('returns null for rubygems.org package miss', async () => {
       const newparams = { ...params };
       newparams.registryUrls = [];
-      got.mockReturnValueOnce({ body: rubygemsOrgVersions });
-      expect(await getPkgReleases(newparams)).toBeNull();
+      httpMock
+        .scope('https://rubygems.org')
+        .get('/versions')
+        .reply(200, rubygemsOrgVersions);
+      const res = await getPkgReleases(newparams);
+      expect(res).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('returns a dep for rubygems.org package hit', async () => {
@@ -66,7 +79,10 @@ describe('datasource/rubygems', () => {
         lookupName: '1pass',
         registryUrls: [],
       };
-      got.mockReturnValueOnce({ body: rubygemsOrgVersions });
+      httpMock
+        .scope('https://rubygems.org')
+        .get('/versions')
+        .reply(200, rubygemsOrgVersions);
       const res = await getPkgReleases(newparams);
       expect(res).not.toBeNull();
       expect(res.releases).toHaveLength(2);
@@ -77,10 +93,14 @@ describe('datasource/rubygems', () => {
       expect(
         res.releases.find((release) => release.version === '0.1.2')
       ).toBeUndefined();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('uses rubygems.org if no registry urls were provided', async () => {
-      got.mockReturnValue({ body: rubygemsOrgVersions });
+      httpMock
+        .scope('https://rubygems.org')
+        .get('/versions')
+        .reply(200, rubygemsOrgVersions);
 
       expect(
         await getPkgReleases({
@@ -97,36 +117,52 @@ describe('datasource/rubygems', () => {
       expect(res).not.toBeNull();
       expect(res.releases).toHaveLength(2);
       expect(res).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('works with real data', async () => {
-      got
-        .mockReturnValueOnce({ body: railsInfo })
-        .mockReturnValueOnce({ body: railsVersions });
+      httpMock
+        .scope('https://thirdparty.com/')
+        .get('/api/v1/gems/rails.json')
+        .reply(200, railsInfo)
+        .get('/api/v1/versions/rails.json')
+        .reply(200, railsVersions);
 
       const res = await getPkgReleases(params);
       expect(res.releases).toHaveLength(339);
       expect(res).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('uses multiple source urls', async () => {
-      got
-        .mockImplementationOnce(() =>
-          Promise.reject({
-            statusCode: 404,
-          })
-        )
-        .mockImplementationOnce(() => ({ body: railsInfo }))
-        .mockImplementationOnce(() => ({ body: railsVersions }));
+      httpMock
+        .scope('https://thirdparty.com/')
+        .get('/api/v1/gems/rails.json')
+        .reply(401);
+      httpMock
+        .scope('https://firstparty.com/')
+        .get('/api/v1/gems/rails.json')
+        .reply(200, railsInfo)
+        .get('/api/v1/versions/rails.json')
+        .reply(200, railsVersions);
 
       const res = await getPkgReleases(params);
       expect(res.releases).toHaveLength(339);
       expect(res).toMatchSnapshot();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
 
     it('returns null if mismatched name', async () => {
-      got.mockReturnValueOnce({ body: { ...railsInfo, name: 'oooops' } });
+      httpMock
+        .scope('https://thirdparty.com/')
+        .get('/api/v1/gems/rails.json')
+        .reply(200, { ...railsInfo, name: 'oooops' });
+      httpMock
+        .scope('https://firstparty.com/')
+        .get('/api/v1/gems/rails.json')
+        .reply(200, null);
       expect(await getPkgReleases(params)).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
   });
 });
diff --git a/lib/datasource/terraform-module/__snapshots__/index.spec.ts.snap b/lib/datasource/terraform-module/__snapshots__/index.spec.ts.snap
index 532181a5425acd2268077ac504e45972d93cbafe..f325481abea80d57aeb0d8620e798e47eddaae73 100644
--- a/lib/datasource/terraform-module/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/terraform-module/__snapshots__/index.spec.ts.snap
@@ -80,6 +80,21 @@ Object {
 }
 `;
 
+exports[`datasource/terraform-module getReleases processes real data 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "registry.terraform.io",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://registry.terraform.io/v1/modules/hashicorp/consul/aws",
+  },
+]
+`;
+
 exports[`datasource/terraform-module getReleases processes with registry in name 1`] = `
 Object {
   "homepage": "https://registry.terraform.io/modules/hashicorp/consul/aws",
@@ -159,3 +174,78 @@ Object {
   "versions": Object {},
 }
 `;
+
+exports[`datasource/terraform-module getReleases processes with registry in name 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "registry.terraform.io",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://registry.terraform.io/v1/modules/hashicorp/consul/aws",
+  },
+]
+`;
+
+exports[`datasource/terraform-module getReleases rejects mismatch 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "terraform.company.com",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://terraform.company.com/v1/modules/consul/foo",
+  },
+]
+`;
+
+exports[`datasource/terraform-module getReleases returns null for 404 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "registry.terraform.io",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://registry.terraform.io/v1/modules/hashicorp/consul/aws",
+  },
+]
+`;
+
+exports[`datasource/terraform-module getReleases returns null for empty result 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "registry.terraform.io",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://registry.terraform.io/v1/modules/hashicorp/consul/aws",
+  },
+]
+`;
+
+exports[`datasource/terraform-module getReleases returns null for unknown error 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "registry.terraform.io",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://registry.terraform.io/v1/modules/hashicorp/consul/aws",
+  },
+]
+`;
diff --git a/lib/datasource/terraform-module/index.spec.ts b/lib/datasource/terraform-module/index.spec.ts
index f6c342eb1b2bca9bf2c0b0830d974335d4a27f78..4d9b4db72d9a2a84c49835799efeada5d2c6bc69 100644
--- a/lib/datasource/terraform-module/index.spec.ts
+++ b/lib/datasource/terraform-module/index.spec.ts
@@ -1,79 +1,95 @@
 import fs from 'fs';
-import _got from '../../util/got';
+import * as httpMock from '../../../test/httpMock';
 import * as terraform from '.';
 
-jest.mock('../../util/got');
-
-const got: any = _got;
-
 const consulData: any = fs.readFileSync(
   'lib/datasource/terraform-module/__fixtures__/registry-consul.json'
 );
 
+const baseUrl = 'https://registry.terraform.io';
+
 describe('datasource/terraform-module', () => {
   describe('getReleases', () => {
     beforeEach(() => {
       jest.clearAllMocks();
+      httpMock.setup();
     });
+
+    afterEach(() => {
+      httpMock.reset();
+    });
+
     it('returns null for empty result', async () => {
-      got.mockReturnValueOnce({ body: {} });
+      httpMock
+        .scope(baseUrl)
+        .get('/v1/modules/hashicorp/consul/aws')
+        .reply(200, {});
       expect(
         await terraform.getReleases({
           lookupName: 'hashicorp/consul/aws',
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for 404', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 404,
-        })
-      );
+      httpMock
+        .scope(baseUrl)
+        .get('/v1/modules/hashicorp/consul/aws')
+        .reply(404, {});
       expect(
         await terraform.getReleases({
           lookupName: 'hashicorp/consul/aws',
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for unknown error', async () => {
-      got.mockImplementationOnce(() => {
-        throw new Error();
-      });
+      httpMock
+        .scope(baseUrl)
+        .get('/v1/modules/hashicorp/consul/aws')
+        .replyWithError('');
       expect(
         await terraform.getReleases({
           lookupName: 'hashicorp/consul/aws',
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real data', async () => {
-      got.mockReturnValueOnce({
-        body: JSON.parse(consulData),
-      });
+      httpMock
+        .scope(baseUrl)
+        .get('/v1/modules/hashicorp/consul/aws')
+        .reply(200, consulData);
       const res = await terraform.getReleases({
         lookupName: 'hashicorp/consul/aws',
       });
       expect(res).toMatchSnapshot();
       expect(res).not.toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes with registry in name', async () => {
-      got.mockReturnValueOnce({
-        body: JSON.parse(consulData),
-      });
+      httpMock
+        .scope(baseUrl)
+        .get('/v1/modules/hashicorp/consul/aws')
+        .reply(200, consulData);
       const res = await terraform.getReleases({
         lookupName: 'registry.terraform.io/hashicorp/consul/aws',
       });
       expect(res).toMatchSnapshot();
       expect(res).not.toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('rejects mismatch', async () => {
-      got.mockReturnValueOnce({
-        body: JSON.parse(consulData),
-      });
+      httpMock
+        .scope('https://terraform.company.com')
+        .get('/v1/modules/consul/foo')
+        .reply(200, consulData);
       const res = await terraform.getReleases({
         lookupName: 'consul/foo',
         registryUrls: ['https://terraform.company.com'],
       });
       expect(res).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
   });
 });
diff --git a/lib/datasource/terraform-provider/__snapshots__/index.spec.ts.snap b/lib/datasource/terraform-provider/__snapshots__/index.spec.ts.snap
index c1eed711879019adc5963af146cd454b8755d8ec..d52991debb8f1f4d4f9bdeade7b2b348262601d4 100644
--- a/lib/datasource/terraform-provider/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/terraform-provider/__snapshots__/index.spec.ts.snap
@@ -205,3 +205,63 @@ Object {
   "versions": Object {},
 }
 `;
+
+exports[`datasource/terraform getReleases processes real data 2`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "registry.terraform.io",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://registry.terraform.io/v1/providers/hashicorp/azurerm",
+  },
+]
+`;
+
+exports[`datasource/terraform getReleases returns null for 404 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "registry.terraform.io",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://registry.terraform.io/v1/providers/hashicorp/azurerm",
+  },
+]
+`;
+
+exports[`datasource/terraform getReleases returns null for empty result 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "registry.terraform.io",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://registry.terraform.io/v1/providers/hashicorp/azurerm",
+  },
+]
+`;
+
+exports[`datasource/terraform getReleases returns null for unknown error 1`] = `
+Array [
+  Object {
+    "headers": Object {
+      "accept": "application/json",
+      "accept-encoding": "gzip, deflate",
+      "host": "registry.terraform.io",
+      "user-agent": "https://github.com/renovatebot/renovate",
+    },
+    "method": "GET",
+    "url": "https://registry.terraform.io/v1/providers/hashicorp/azurerm",
+  },
+]
+`;
diff --git a/lib/datasource/terraform-provider/index.spec.ts b/lib/datasource/terraform-provider/index.spec.ts
index 6eb64b723f9162523a3d30659c4b81a96c574c90..f730f0de25340bc79c2ee1fb0731a0cb7b521dde 100644
--- a/lib/datasource/terraform-provider/index.spec.ts
+++ b/lib/datasource/terraform-provider/index.spec.ts
@@ -1,59 +1,68 @@
 import fs from 'fs';
-import _got from '../../util/got';
+import * as httpMock from '../../../test/httpMock';
 import * as terraformProvider from '.';
 
-jest.mock('../../util/got');
-
-const got: any = _got;
-
 const consulData: any = fs.readFileSync(
   'lib/datasource/terraform-provider/__fixtures__/azurerm-provider.json'
 );
 
+const baseUrl = 'https://registry.terraform.io/';
+
 describe('datasource/terraform', () => {
   describe('getReleases', () => {
     beforeEach(() => {
       jest.clearAllMocks();
+      httpMock.setup();
+    });
+
+    afterEach(() => {
+      httpMock.reset();
     });
+
     it('returns null for empty result', async () => {
-      got.mockReturnValueOnce({ body: {} });
+      httpMock
+        .scope(baseUrl)
+        .get('/v1/providers/hashicorp/azurerm')
+        .reply(200, {});
       expect(
         await terraformProvider.getReleases({
           lookupName: 'azurerm',
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for 404', async () => {
-      got.mockImplementationOnce(() =>
-        Promise.reject({
-          statusCode: 404,
-        })
-      );
+      httpMock.scope(baseUrl).get('/v1/providers/hashicorp/azurerm').reply(404);
       expect(
         await terraformProvider.getReleases({
           lookupName: 'azurerm',
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('returns null for unknown error', async () => {
-      got.mockImplementationOnce(() => {
-        throw new Error();
-      });
+      httpMock
+        .scope(baseUrl)
+        .get('/v1/providers/hashicorp/azurerm')
+        .replyWithError('');
       expect(
         await terraformProvider.getReleases({
           lookupName: 'azurerm',
         })
       ).toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
     it('processes real data', async () => {
-      got.mockReturnValueOnce({
-        body: JSON.parse(consulData),
-      });
+      httpMock
+        .scope(baseUrl)
+        .get('/v1/providers/hashicorp/azurerm')
+        .reply(200, JSON.parse(consulData));
       const res = await terraformProvider.getReleases({
         lookupName: 'azurerm',
       });
       expect(res).toMatchSnapshot();
       expect(res).not.toBeNull();
+      expect(httpMock.getTrace()).toMatchSnapshot();
     });
   });
 });
diff --git a/lib/platform/bitbucket-server/index.spec.ts b/lib/platform/bitbucket-server/index.spec.ts
index 090ed692ba7e4eb9b9142d848f3afa110ac3eb23..5a824b49d9bd9a847917838f881ff72d3877c090 100644
--- a/lib/platform/bitbucket-server/index.spec.ts
+++ b/lib/platform/bitbucket-server/index.spec.ts
@@ -975,7 +975,6 @@ describe('platform/bitbucket-server', () => {
             topic: null,
             content: '!merge',
           });
-          httpMock.getTrace();
           expect(res).toBe(true);
           expect(httpMock.getTrace()).toMatchSnapshot();
         });
diff --git a/lib/platform/bitbucket/comments.spec.ts b/lib/platform/bitbucket/comments.spec.ts
index 7299f9eb58ba3aae91bfdb559e634412e8c8d890..79914fd15a2bcb131e044d24c2f2ca5fe9124986 100644
--- a/lib/platform/bitbucket/comments.spec.ts
+++ b/lib/platform/bitbucket/comments.spec.ts
@@ -75,7 +75,6 @@ describe('platform/comments', () => {
         topic: 'some-subject',
         content: 'some\ncontent',
       });
-      httpMock.getTrace();
       expect(res).toBe(true);
       expect(httpMock.getTrace()).toMatchSnapshot();
     });
diff --git a/lib/util/http/gitea.spec.ts b/lib/util/http/gitea.spec.ts
index bb5ecd2d3c5a82ba1fd2dc9d8dd848a94ddce9f7..47de55353a5e8f8e2474cb308bca99383f93c2d7 100644
--- a/lib/util/http/gitea.spec.ts
+++ b/lib/util/http/gitea.spec.ts
@@ -44,7 +44,6 @@ describe(getName(__filename), () => {
     const res = await giteaHttp.getJson(`${baseUrl}/pagination-example-1`, {
       paginate: true,
     });
-    httpMock.getTrace();
 
     expect(res.body).toHaveLength(6);
     expect(res.body).toEqual(['abc', 'def', 'ghi', 'jkl', 'mno', 'pqr']);
diff --git a/lib/workers/pr/changelog/release-notes.spec.ts b/lib/workers/pr/changelog/release-notes.spec.ts
index 6221246ebd58377e582bb36913a632fbfeef4303..484773d0c833537b32bc6a599475d5d7da010f12 100644
--- a/lib/workers/pr/changelog/release-notes.spec.ts
+++ b/lib/workers/pr/changelog/release-notes.spec.ts
@@ -298,7 +298,6 @@ describe('workers/pr/release-notes', () => {
         'https://gitlab.com/',
         'https://api.gitlab.com/'
       );
-      httpMock.getTrace();
       expect(res).not.toBeNull();
       expect(res).toMatchSnapshot();
       expect(httpMock.getTrace()).toMatchSnapshot();