diff --git a/packages/utils/testing/src/embark.js b/packages/utils/testing/src/embark.js
index cb4b1ae83..4c4c7fd90 100644
--- a/packages/utils/testing/src/embark.js
+++ b/packages/utils/testing/src/embark.js
@@ -2,11 +2,13 @@ const sinon = require('sinon');
 import fs from 'fs-extra';
 
 class Embark {
-  constructor(events, plugins, config) {
+  constructor(events, plugins, config = {}, ipc) {
     this.events = events;
     this.plugins = plugins;
     this.config = config || {};
     this.config.plugins = plugins;
+    this.ipc = ipc;
+    this.config.ipc = ipc;
     this.assert = new EmbarkAssert(this);
     this.fs = fs;
 
@@ -32,12 +34,14 @@ class Embark {
   }
 
   teardown() {
-    this.config = { plugins: this.plugins };
+    this.config = { plugins: this.plugins, ipc: this.ipc };
     this.plugins.teardown();
+    this.ipc.teardown();
+    this.events.teardown();
   }
 
   setConfig(config) {
-    this.config = { ...config, plugins: this.plugins };
+    this.config = { ...config, plugins: this.plugins, ipc: this.ipc };
   }
 }
 
diff --git a/packages/utils/testing/src/events.js b/packages/utils/testing/src/events.js
index bd526a6db..0e617f23e 100644
--- a/packages/utils/testing/src/events.js
+++ b/packages/utils/testing/src/events.js
@@ -5,6 +5,7 @@ class Events {
   constructor() {
     this.commandHandlers = {};
     this.handlers = {};
+    this.emissions = {};
 
     this.assert = new EventsAssert(this);
   }
@@ -18,14 +19,13 @@ class Events {
       this.handlers[ev] = [];
     }
 
-    this.handlers[ev].push(cb);
+    this.handlers[ev].push(sinon.spy(cb));
   }
 
-  emit() {
+  emit(ev, ...args) {
 
-  }
+    this.emissions[ev] = args;
 
-  trigger(ev, ...args) {
     if (!this.handlers[ev]) {
       return;
     }
@@ -34,12 +34,12 @@ class Events {
   }
 
   request(cmd, ...args) {
-    assert(this.commandHandlers[cmd], `command handler for ${cmd} not registered`);
+    assert(this.commandHandlers[cmd], `command handler for '${cmd}' not registered`);
     Promise.resolve(this.commandHandlers[cmd](...args));
   }
 
   request2(cmd, ...args) {
-    assert(this.commandHandlers[cmd], `command handler for ${cmd} not registered`);
+    assert(this.commandHandlers[cmd], `command handler for '${cmd}' not registered`);
     return new Promise((resolve, reject) => {
       args.push((err, ...res) => {
         if (err) {
@@ -53,6 +53,12 @@ class Events {
       this.commandHandlers[cmd](...args);
     });
   }
+
+  teardown() {
+    this.commandHandlers = {};
+    this.handlers = {};
+    this.emissions = {};
+  }
 }
 
 class EventsAssert {
@@ -61,19 +67,40 @@ class EventsAssert {
   }
 
   commandHandlerRegistered(cmd) {
-    assert(this.events.commandHandlers[cmd], `command handler for ${cmd} wanted, but not registered`);
+    assert(this.events.commandHandlers[cmd], `command handler for '${cmd}' wanted, but not registered`);
   }
 
   commandHandlerCalled(cmd) {
     this.commandHandlerRegistered(cmd);
     sinon.assert.called(this.events.commandHandlers[cmd]);
   }
+  
+  commandHandlerNotCalled(cmd) {
+    this.commandHandlerRegistered(cmd);
+    assert(!this.events.commandHandlers[cmd].called);
+  }
 
   commandHandlerCalledWith(cmd, ...args) {
     this.commandHandlerRegistered(cmd);
     sinon.assert.calledWith(this.events.commandHandlers[cmd], ...args);
   }
 
+  listenerRegistered(name) {
+    assert(this.events.handlers[name], `event listener for '${name}' wanted, but not registered`);
+  }
+
+  emitted(name) {
+    assert(this.events.emissions[name]);
+  }
+
+  notEmitted(name) {
+    assert(!Object.keys(this.events.emissions).includes(name));
+  }
+
+  emittedWith(name, ...args) {
+    assert.equal(this.events.emissions[name], ...args);
+  }
+
 }
 
 module.exports = Events;
diff --git a/packages/utils/testing/src/index.js b/packages/utils/testing/src/index.js
index a69d42ac9..9e4fde344 100644
--- a/packages/utils/testing/src/index.js
+++ b/packages/utils/testing/src/index.js
@@ -2,22 +2,14 @@ const Embark = require('./embark');
 const Events = require('./events');
 const Plugins = require('./plugin');
 const HttpMockServer = require('./httpServer');
+const Ipc = require('./ipc');
 
-const fakeEmbark = (config) => {
+const fakeEmbark = (config = {}) => {
   const events = new Events();
   const plugins = new Plugins();
+  const ipc = config.ipc ?? new Ipc();
 
-  const ipc = {
-    isServer: () => { return true; },
-    broadcast: () => {},
-    on: () => {},
-    isClient: () => { return false; }
-  };
-
-  config = config || {};
-  config.ipc = config.ipc || ipc;
-
-  const embark = new Embark(events, plugins, config);
+  const embark = new Embark(events, plugins, config, ipc);
   return {
     embark,
     plugins
@@ -29,6 +21,6 @@ module.exports = {
   Events,
   Plugins,
   HttpMockServer,
-
+  Ipc,
   fakeEmbark
 };
diff --git a/packages/utils/testing/src/lib/ipc.js b/packages/utils/testing/src/lib/ipc.js
new file mode 100644
index 000000000..164a73f73
--- /dev/null
+++ b/packages/utils/testing/src/lib/ipc.js
@@ -0,0 +1,85 @@
+const assert = require('assert');
+const sinon = require('sinon');
+
+class Ipc {
+  constructor(isServer = true) {
+    this._isServer = isServer;
+    this.handlers = {};
+    this.assert = new IpcAssert(this);
+    this._client = null;
+    this.broadcasts = {};
+  }
+
+  get connected() {
+    return true;
+  }
+
+  get client() {
+    if (this._client !== null) {
+      return this._client;
+    }
+    this._client = new Ipc(false);
+    return this._client;
+  }
+
+  broadcast(ev, ...args) {
+    this.broadcasts[ev] = args;
+  }
+
+  isClient() {
+    return !this._isServer;
+  }
+
+  isServer() {
+    return this._isServer;
+  }
+
+  on(ev, cb) {
+    if (!this.handlers[ev]) {
+      this.handlers[ev] = [];
+    }
+
+    this.handlers[ev].push(cb);
+  }
+
+  request(ev, ...args) {
+    if (!this.handlers[ev]) {
+      return;
+    }
+
+    this.handlers[ev].forEach(h => h(...args));
+  }
+
+  teardown() {
+    this.handlers = {};
+    this._client = null;
+    this.broadcasts = {};
+  }
+}
+
+class IpcAssert {
+  constructor(ipc) {
+    this.ipc = ipc;
+  }
+
+  listenerRegistered(cmd) {
+    assert(this.ipc.handlers[cmd], `listener for '${cmd}' wanted, but not registered`);
+  }
+  
+  listenerNotRegistered(cmd) {
+    assert(!Object.keys(this.ipc.handlers).some(command => command === cmd), `listener for '${cmd}' registered, but expected to not be registered`);
+  }
+  
+  listenerRequested(cmd) {
+    this.listenerRegistered(cmd);
+    sinon.assert.called(this.events.handlers[cmd]);
+  }
+
+  listenerRequestedWith(cmd, ...args) {
+    this.listenerRequested(cmd);
+    sinon.assert.calledWith(this.events.handlers[cmd], ...args);
+  }
+
+}
+
+module.exports = Ipc;
diff --git a/packages/utils/testing/src/plugin.js b/packages/utils/testing/src/plugin.js
index be9caefa3..27c6d94fe 100644
--- a/packages/utils/testing/src/plugin.js
+++ b/packages/utils/testing/src/plugin.js
@@ -46,7 +46,7 @@ class Plugins {
   }
 
   teardown() {
-    this.plugin.listeners = {};
+    this.plugin.teardown();
     this.plugins.forEach(plugin => plugin.teardown());
   }
 
@@ -108,6 +108,10 @@ class Plugin {
   }
 
   teardown() {
+    this.listeners = {};
+    this.apiCalls = {};
+    this.pluginTypes = [];
+    this.console = [];
     this.compilers = [];
   }
 }
@@ -135,6 +139,22 @@ class PluginsAssert {
     const index = (method + endpoint).toLowerCase();
     assert(this.plugins.plugin.apiCalls[index], `API call for '${method} ${endpoint}' wanted, but not registered`);
   }
+  
+  consoleCommandRegistered(command) {
+    const registered = this.plugins.plugin.console.some(cmd => {
+      if (!cmd.matches) {
+        return false;
+      }
+      if (Array.isArray(cmd.matches)) {
+        return cmd.matches.some(matches => matches.includes(command));
+      }
+      if (typeof cmd.matches === 'function') {
+        return cmd.matches(command);
+      }
+      return false;
+    });
+    assert(registered);
+  }
 }
 
 class PluginsMock {
@@ -142,6 +162,38 @@ class PluginsMock {
     this.plugins = plugins;
   }
 
+  consoleCommand(cmd) {
+    const command = this.plugins.plugin.console.find(c => {
+      if (!c.matches) {
+        return;
+      }
+      if (Array.isArray(c.matches) && c.matches.some(matches => matches.includes(cmd))) {
+        return c;
+      }
+      if (typeof c.matches === 'function' && c.matches(cmd)) {
+        return c;
+      }
+      return;
+    });
+    assert(command, `Console command for '${cmd}' wanted, but not registered`);
+    const cb = sinon.fake();
+
+    return new Promise((resolve, reject) => {
+      command.process(cmd, (err, ...res) => {
+        if (err) {
+          return reject(err);
+        }
+        if (res.length && res.length > 1) {
+          cb(res);
+          return resolve(cb);
+        }
+        cb(res[0]);
+        return resolve(cb);
+      });
+      resolve(cb);
+    });
+  }
+
   async apiCall(method, endpoint, params) {
     const index = (method + endpoint).toLowerCase();
     const apiFn = this.plugins.plugin.apiCalls[index];