diff --git a/cucumber.js b/cucumber.js
index 026460b84aa59298a096b1fc965232b44f848f7a..02d6548d452261f96aad2b7fed5e6f5e1e857abd 100644
--- a/cucumber.js
+++ b/cucumber.js
@@ -1,3 +1,9 @@
 module.exports = {
-  default: `--format-options '{"snippetInterface": "synchronous"}'`
-}
\ No newline at end of file
+  default: {
+    formatOptions: {
+      snippetInterface: "synchronous"
+    },
+    paths: [ 'features/**/*.feature' ],
+    require: [ 'test/cucumber/**/*.steps.js' ],
+  },
+};
diff --git a/dist/adapter.esm.js b/dist/adapter.esm.js
index d79a030d469bd3fde4ab8fe19dad0a056f8ed65e..d1375d79711b391d24dd4a450e40e7c39679edcb 100644
--- a/dist/adapter.esm.js
+++ b/dist/adapter.esm.js
@@ -1,4 +1,3 @@
-import Validator from 'swagger-model-validator';
 import axios from 'axios';
 
 /**
@@ -9,7 +8,6 @@ class Adapter {
     this.protocol = protocol;
     this.config = config;
     this.axios = axios;
-    this.validator = new Validator(protocol.schema);
   }
 
   /**
@@ -25,13 +23,7 @@ class Adapter {
    * @returns {object}
    */
   validate(message) {
-    return this.validator.validate(
-      message,
-      this.protocol.schema.components.schemas.Message,
-      this.protocol.schema.components.schemas,
-      false,
-      true
-    );
+    return this.protocol.validate(message);
   }
 
   /**
@@ -113,8 +105,7 @@ class Adapter {
         return response;
       })
       .catch((error) => {
-        console.error(error);
-        return Promise.reject(error.response);
+        return Promise.reject(error);
       });
   }
 
@@ -145,7 +136,7 @@ class Adapter {
         return response;
       })
       .catch((error) => {
-        return Promise.reject(error.response);
+        return Promise.reject(error);
       });
   }
 
@@ -178,7 +169,7 @@ class Adapter {
         return response;
       })
       .catch((error) => {
-        return Promise.reject(error.response);
+        return Promise.reject(error);
       });
   }
 }
diff --git a/dist/adapter.js b/dist/adapter.js
index 565f78dbfd4301d50e51ddcf7981f1c69943232d..8471c08bd7c2e6e46adc4b88bd58d89c6f25865d 100644
--- a/dist/adapter.js
+++ b/dist/adapter.js
@@ -1,6 +1,5 @@
 'use strict';
 
-var Validator = require('swagger-model-validator');
 var axios = require('axios');
 
 /**
@@ -11,7 +10,6 @@ class Adapter {
     this.protocol = protocol;
     this.config = config;
     this.axios = axios;
-    this.validator = new Validator(protocol.schema);
   }
 
   /**
@@ -27,13 +25,7 @@ class Adapter {
    * @returns {object}
    */
   validate(message) {
-    return this.validator.validate(
-      message,
-      this.protocol.schema.components.schemas.Message,
-      this.protocol.schema.components.schemas,
-      false,
-      true
-    );
+    return this.protocol.validate(message);
   }
 
   /**
@@ -115,8 +107,7 @@ class Adapter {
         return response;
       })
       .catch((error) => {
-        console.error(error);
-        return Promise.reject(error.response);
+        return Promise.reject(error);
       });
   }
 
@@ -147,7 +138,7 @@ class Adapter {
         return response;
       })
       .catch((error) => {
-        return Promise.reject(error.response);
+        return Promise.reject(error);
       });
   }
 
@@ -180,7 +171,7 @@ class Adapter {
         return response;
       })
       .catch((error) => {
-        return Promise.reject(error.response);
+        return Promise.reject(error);
       });
   }
 }
diff --git a/dist/protocol.esm.js b/dist/protocol.esm.js
index 0b9a29f0b53148df63a5751b5fafdd8cb1d6a27a..3bf81eb3c7112b251af0953d7a71517b88fe1477 100644
--- a/dist/protocol.esm.js
+++ b/dist/protocol.esm.js
@@ -37,8 +37,8 @@ class GenericProtocol {
   validate(message) {
     return this.validator.validate(
       message,
-      this.protocol.schema.components.schemas.Message,
-      this.protocol.schema.components.schemas,
+      this.schema.components.schemas.Message,
+      this.schema.components.schemas,
       false,
       true
     );
diff --git a/dist/protocol.js b/dist/protocol.js
index c5004e1f84c4cfe22664804963513cc6fd43a744..f5e618c4e13101919101429ef372ec5b7f958ef0 100644
--- a/dist/protocol.js
+++ b/dist/protocol.js
@@ -39,8 +39,8 @@ class GenericProtocol {
   validate(message) {
     return this.validator.validate(
       message,
-      this.protocol.schema.components.schemas.Message,
-      this.protocol.schema.components.schemas,
+      this.schema.components.schemas.Message,
+      this.schema.components.schemas,
       false,
       true
     );
diff --git a/features/adapter/auth.js b/features/adapter/auth.js
deleted file mode 100644
index 4a64298638c8e7ba476739f09325a5c2c98b7f72..0000000000000000000000000000000000000000
--- a/features/adapter/auth.js
+++ /dev/null
@@ -1,72 +0,0 @@
-const assert = require('assert');
-const { Before, Given, When, Then } = require('@cucumber/cucumber');
-
-const axios = require("axios");
-const MockAdapter = require("axios-mock-adapter");
-
-// This sets the mock adapter on the default instance
-const mockAxios = new MockAdapter(axios);
-
-const { fixtures } = require('../../test/fixtures/server');
-
-const mockValidConfig = fixtures.get('config-valid');
-const mockInvalidConfig = fixtures.get('config-invalid');
-
-const mockSchema = require('../../test/mock/swagger.json');
-const { Adapter } = require('../../dist/adapter');
-const { GenericProtocol } = require('../../dist/protocol');
-
-
-let decodeTracker;
-class TrackedGenericProtocol extends GenericProtocol {
-  decode() {
-    return super.decode()
-  }
-}
-
-Before(function() {
-  this.mockAxios = mockAxios;
-  this.mockAxios.reset();
-
-  this.mockAxios.onGet(
-    `${mockValidConfig.api}/token`, 
-    { params: { client_id: mockValidConfig.client_id, secret: mockValidConfig.secret } }
-  ).reply(200, fixtures.get('response-valid-token'));
-
-  this.mockAxios.onGet(
-    `${mockInvalidConfig.api}/token`, 
-    { params: { client_id: mockInvalidConfig.client_id, secret: mockInvalidConfig.secret } }
-  ).reply(403, fixtures.get('response-denied-token'));
-
-});
-
-Given('valid config', function() {
-  this.schema = mockSchema;
-  this.config = mockValidConfig
-});
-
-When('the adapter instance is created', function() {
-  let mockProtocol = new GenericProtocol(this.schema);
-  let mockAdapter = new Adapter(mockProtocol, this.config);
-  this.adapter = mockAdapter;
-});
-
-When('the auth method is called', async function() {
-  await this.adapter.auth();
-});
-
-Then(('the adapter credentials are populated'), function() { 
-  assert.equal(this.adapter.credentials.token, fixtures.get('response-valid-token').token);
-});
-
-Given('invalid config', function() {
-  this.schema = mockSchema;
-  this.config = mockInvalidConfig;
-});
-
-Then('the adapter auth fails', async function() {
-  this.adapter.auth()
-  .catch((error) => {
-    assert.equal(error.response.status, 403);
-  });
-});
\ No newline at end of file
diff --git a/features/adapter/poll.js b/features/adapter/poll.js
deleted file mode 100644
index 76c537abe675d25078e0d3d5732f5109300b59b7..0000000000000000000000000000000000000000
--- a/features/adapter/poll.js
+++ /dev/null
@@ -1,36 +0,0 @@
-const assert = require('assert');
-const { Before, Given, When, Then } = require('@cucumber/cucumber');
-
-const { fixtures } = require('../../test/fixtures/server');
-
-const mockValidConfig = fixtures.get('config-valid');
-
-const xMessageResponse = function(xMessages) {
-  const message = fixtures.get('message-vehicle-status');
-  let response = [];
-  for (let i=0; i<xMessages; i++) {
-    response.push({
-      topic: "broadcast",
-      message: JSON.stringify(message)
-    });
-  }
-  return response;
-};
-
-When('the queue contains {int} messages', function(xMessages) {
-  const response = xMessageResponse(xMessages);
-  this.mockAxios.onGet(
-    `${mockValidConfig.api}/receive`, 
-  ).reply(200, response);
-});
-
-When('the poll method is called', async function() {
-  this.messages = await this.adapter.poll()
-  .then((response) => {
-    return response.data;
-  });
-});
-
-Then('a successful response is returned with {int} messages', function(xMessages) {
-  assert.equal(this.messages.length, xMessages);
-});
\ No newline at end of file
diff --git a/features/adapter_authenticates.feature b/features/adapter_auth.feature
similarity index 100%
rename from features/adapter_authenticates.feature
rename to features/adapter_auth.feature
diff --git a/features/adapter_receives.feature b/features/adapter_poll.feature
similarity index 57%
rename from features/adapter_receives.feature
rename to features/adapter_poll.feature
index 6582ca348bf392cf0ebd6f636e3564f0f2304ce5..9ecc9e9553dd86c54bda7aa66d36950eed619741 100644
--- a/features/adapter_receives.feature
+++ b/features/adapter_poll.feature
@@ -9,7 +9,7 @@ Feature: Can the adapter receive messages?
     Given valid config
     When the adapter instance is created
     When the auth method is called
-    When the queue contains 0 messages
+    When a mock receive API response is configured to return 0 messages
     When the poll method is called
     Then a successful response is returned with 0 messages  
 
@@ -17,14 +17,26 @@ Feature: Can the adapter receive messages?
     Given valid config
     When the adapter instance is created
     When the auth method is called
-    When the queue contains 2 messages 
+    When a mock receive API response is configured to return 2 messages 
     When the poll method is called
     Then a successful response is returned with 2 messages
+    Then the protocol "validate" method is called 2 times
+    Then the protocol "decode" method is called 2 times
 
   Scenario: 10 messages are received succecssfully if the queue contains 10 messages
     Given valid config
     When the adapter instance is created
     When the auth method is called
-    When the queue contains 10 messages 
+    When a mock receive API response is configured to return 10 messages 
     When the poll method is called
     Then a successful response is returned with 10 messages
+    Then the protocol "validate" method is called 10 times
+    Then the protocol "decode" method is called 10 times
+
+  Scenario: An invalid token returns a forbidden response 
+    Given valid config 
+    When the adapter instance is created 
+    When the auth method is called 
+    When a mock receive API response is configured to return an error
+    When the poll method is called 
+    Then an error response is returned with status 403
diff --git a/features/adapter_publish.feature b/features/adapter_publish.feature
new file mode 100644
index 0000000000000000000000000000000000000000..7e7e7de1681a3220f61b829b457e7e35904ca7c4
--- /dev/null
+++ b/features/adapter_publish.feature
@@ -0,0 +1,18 @@
+Feature: Can the adapter publish messages?
+  The adapter publish method works as expected
+
+  Scenario: A message can be published successfully
+    Given valid config
+    When the adapter instance is created
+    When the auth method is called
+    When a mock send API response is configured to return success
+    When the publish method is called
+    Then a successful response is returned with status 200
+
+  Scenario: A failed publish returns a 403
+    Given valid config
+    When the adapter instance is created
+    When the auth method is called
+    When a mock send API response is configured to return an error
+    When the publish method is called
+    Then an error response is returned with status 403
diff --git a/features/adapter_validates.feature b/features/adapter_validate.feature
similarity index 100%
rename from features/adapter_validates.feature
rename to features/adapter_validate.feature
diff --git a/features/schema_validates.feature b/features/schema_validate.feature
similarity index 100%
rename from features/schema_validates.feature
rename to features/schema_validate.feature
diff --git a/package.json b/package.json
index 90ecb8937ae25cb72e8db21c759563ad50f10001..e48afd31bf2f76a86c06ce1b9da0c9e3d8139986 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,8 @@
     "lint": "yarn lint:js && yarn lint:prettier",
     "lintfix": "prettier --write --list-different . && yarn lint:js --fix",
     "prepare": "husky install",
-    "test": "yarn build && yarn cucumber-js",
+    "test": "yarn build && yarn cucumber",
+    "cucumber": "cucumber-js",
     "build": "cross-env NODE_ENV=production rollup -c"
   },
   "lint-staged": {
diff --git a/src/adapter/index.js b/src/adapter/index.js
index ad41f54b7132c5101bc435ce72e0fa4fde02592a..b4dc835da3d6987198bd2ae3e185a4bef200efec 100644
--- a/src/adapter/index.js
+++ b/src/adapter/index.js
@@ -1,4 +1,3 @@
-import Validator from 'swagger-model-validator';
 import axios from 'axios';
 
 /**
@@ -9,7 +8,6 @@ export class Adapter {
     this.protocol = protocol;
     this.config = config;
     this.axios = axios;
-    this.validator = new Validator(protocol.schema);
   }
 
   /**
@@ -25,13 +23,7 @@ export class Adapter {
    * @returns {object}
    */
   validate(message) {
-    return this.validator.validate(
-      message,
-      this.protocol.schema.components.schemas.Message,
-      this.protocol.schema.components.schemas,
-      false,
-      true
-    );
+    return this.protocol.validate(message);
   }
 
   /**
@@ -113,8 +105,7 @@ export class Adapter {
         return response;
       })
       .catch((error) => {
-        console.error(error);
-        return Promise.reject(error.response);
+        return Promise.reject(error);
       });
   }
 
@@ -145,7 +136,7 @@ export class Adapter {
         return response;
       })
       .catch((error) => {
-        return Promise.reject(error.response);
+        return Promise.reject(error);
       });
   }
 
@@ -178,7 +169,7 @@ export class Adapter {
         return response;
       })
       .catch((error) => {
-        return Promise.reject(error.response);
+        return Promise.reject(error);
       });
   }
 }
diff --git a/src/protocol/index.js b/src/protocol/index.js
index 74676ddb91706e411b78024dc8ad3e3081995dd5..646194a6b0b046b5ce65c7ca528c7646f1d8f361 100644
--- a/src/protocol/index.js
+++ b/src/protocol/index.js
@@ -37,8 +37,8 @@ export class GenericProtocol {
   validate(message) {
     return this.validator.validate(
       message,
-      this.protocol.schema.components.schemas.Message,
-      this.protocol.schema.components.schemas,
+      this.schema.components.schemas.Message,
+      this.schema.components.schemas,
       false,
       true
     );
diff --git a/test/cucumber/adapter/auth.steps.js b/test/cucumber/adapter/auth.steps.js
new file mode 100644
index 0000000000000000000000000000000000000000..110d21ce7e617fa2c3f1fd8c8b29079508efac73
--- /dev/null
+++ b/test/cucumber/adapter/auth.steps.js
@@ -0,0 +1,41 @@
+const assert = require('assert');
+const { Given, When, Then } = require('@cucumber/cucumber');
+
+const { fixtures } = require('../../fixtures/server');
+
+const mockValidConfig = fixtures.get('config-valid');
+const mockInvalidConfig = fixtures.get('config-invalid');
+
+const mockSchema = require('../../mock/swagger.json');
+const { Adapter } = require('../../../dist/adapter');
+
+
+Given('valid config', function() {
+  this.config = mockValidConfig
+});
+
+When('the adapter instance is created', function() {
+  //let mockProtocol = new GenericProtocol(this.schema);
+  let mockAdapter = new Adapter(this.protocol, this.config);
+  this.adapter = mockAdapter;
+});
+
+When('the auth method is called', async function() {
+  await this.adapter.auth();
+});
+
+Then('the adapter credentials are populated', function() { 
+  assert.equal(this.adapter.credentials.token, fixtures.get('response-valid-token').token);
+});
+
+Given('invalid config', function() {
+  this.schema = mockSchema;
+  this.config = mockInvalidConfig;
+});
+
+Then('the adapter auth fails', function() {
+  this.adapter.auth()
+  .catch((error) => {
+    assert.equal(error.response.status, 403);
+  });
+});
\ No newline at end of file
diff --git a/test/cucumber/adapter/before.steps.js b/test/cucumber/adapter/before.steps.js
new file mode 100644
index 0000000000000000000000000000000000000000..9471285eb547e173f243c4af1a42add30edf807f
--- /dev/null
+++ b/test/cucumber/adapter/before.steps.js
@@ -0,0 +1,81 @@
+const assert = require('assert');
+const { Before } = require('@cucumber/cucumber');
+
+const axios = require("axios");
+const MockAdapter = require("axios-mock-adapter");
+
+// This sets the mock adapter on the default instance
+const mockAxios = new MockAdapter(axios);
+
+const { fixtures } = require('../../fixtures/server');
+
+const mockValidConfig = fixtures.get('config-valid');
+const mockInvalidConfig = fixtures.get('config-invalid');
+
+const mockSchema = require('../../mock/swagger.json');
+const { GenericProtocol } = require('../../../dist/protocol');
+
+/**
+ * Use assert.CallTracker to track internal method calls 
+ * Instead of adding trackers to each method, create a 
+ * single tracked stub function and then use the parameters
+ * to record what is being tracked. 
+ */
+const tracker = new assert.CallTracker();
+const trackedFunction = function(method, params) {
+  // do nothing;
+};
+const recorder = tracker.calls(trackedFunction);
+
+class TrackedGenericProtocol extends GenericProtocol {
+  constructor(schema, services) {
+    super(schema, services);
+    this.recorder = services.recorder;
+    this.tracker = services.tracker;
+  }
+  encode(type, message) {
+    this.recorder('encode', {type, message});
+    return super.encode(type, message)
+  }
+  decode(type, message) {
+    this.recorder('decode', {type, message});
+    return super.decode(type, message)
+  }
+  validate(message) {
+    this.recorder('validate', {message});
+    return super.validate(message);
+  }
+  getTrackedCalls(method) {
+    let calls = this.tracker.getCalls(this.recorder);
+    let methodCalls = calls.filter(call => call.arguments[0] === method);
+    return methodCalls;
+  }
+  resetTracker() {
+    this.tracker.reset();
+  }
+}
+
+Before(function() {
+  this.schema = mockSchema;
+  this.tracker = tracker;
+  this.recorder = recorder;
+  let services = {
+    recorder: this.recorder,
+    tracker: this.tracker
+  };
+  this.protocol = new TrackedGenericProtocol(this.schema, services);
+  this.protocol.resetTracker();
+  this.mockAxios = mockAxios;
+  this.mockAxios.reset();
+
+  this.mockAxios.onGet(
+    `${mockValidConfig.api}/token`, 
+    { params: { client_id: mockValidConfig.client_id, secret: mockValidConfig.secret } }
+  ).reply(200, fixtures.get('response-valid-token'));
+
+  this.mockAxios.onGet(
+    `${mockInvalidConfig.api}/token`, 
+    { params: { client_id: mockInvalidConfig.client_id, secret: mockInvalidConfig.secret } }
+  ).reply(403, fixtures.get('response-denied-token'));
+
+});
\ No newline at end of file
diff --git a/test/cucumber/adapter/poll.steps.js b/test/cucumber/adapter/poll.steps.js
new file mode 100644
index 0000000000000000000000000000000000000000..3d7ddaf7c3d68532947b627f9c44dc2d08afa19c
--- /dev/null
+++ b/test/cucumber/adapter/poll.steps.js
@@ -0,0 +1,54 @@
+const assert = require('assert');
+const { When, Then } = require('@cucumber/cucumber');
+
+const { fixtures } = require('../../fixtures/server');
+
+const mockValidConfig = fixtures.get('config-valid');
+
+const xMessageResponse = function(xMessages) {
+  const message = fixtures.get('message-vehicle-status');
+  let response = [];
+  for (let i=0; i<xMessages; i++) {
+    response.push({
+      topic: "broadcast",
+      message: JSON.stringify(message)
+    });
+  }
+  return response;
+};
+
+When('a mock receive API response is configured to return {int} messages', function(xMessages) {
+  const response = xMessageResponse(xMessages);
+  this.mockAxios.onGet(
+    `${mockValidConfig.api}/receive`, 
+  ).reply(200, response);
+});
+
+When('a mock receive API response is configured to return an error', function() {
+  this.mockAxios.onGet(
+    `${mockValidConfig.api}/receive`,
+  ).reply(403, { message: 'Token expired' })
+});
+
+When('the poll method is called', function() {
+  this.call = this.adapter.poll();
+});
+
+Then('a successful response is returned with {int} messages', function(xMessages) {
+  this.call
+  .then(response => {
+    assert.equal(response.data.length, xMessages);
+  });
+});
+
+Then('the protocol {string} method is called {int} times', function(method, xInvokes) {
+  const decodes = this.protocol.getTrackedCalls(method);
+  assert.equal(decodes.length, xInvokes);
+});
+
+Then('an error response is returned with status {int}', function(expectedStatus) {
+  this.call
+  .catch((error) => {
+    assert.equal(error.response.status, expectedStatus);
+  });
+});
diff --git a/test/cucumber/adapter/publish.steps.js b/test/cucumber/adapter/publish.steps.js
new file mode 100644
index 0000000000000000000000000000000000000000..055c7fd12a483f2aa980798d4c9435e44370dac2
--- /dev/null
+++ b/test/cucumber/adapter/publish.steps.js
@@ -0,0 +1,34 @@
+const assert = require('assert');
+const { When, Then } = require('@cucumber/cucumber');
+
+const { fixtures } = require('../../fixtures/server');
+
+const mockValidConfig = fixtures.get('config-valid');
+
+When('a mock send API response is configured to return success', function() {
+  const response = {};
+  this.mockAxios.onPost(
+    `${mockValidConfig.api}/send`, 
+  ).reply(200, response);
+});
+
+When('a mock send API response is configured to return an error', function() {
+  this.mockAxios.onPost(
+    `${mockValidConfig.api}/send`,
+  ).reply(403, { message: 'Token expired' })
+});
+
+When('the publish method is called', function() {
+  const message = fixtures.get('message-vehicle-status');
+  this.message = message;
+  const topic = message.metadata.destination;
+  const body = JSON.stringify(message);
+  this.call = this.adapter.publish(topic, body);
+});
+
+Then('a successful response is returned with status {int}', function(expectedStatus) {
+  this.call
+  .then(response => {
+    assert.equal(response.status, expectedStatus);
+  });
+});
\ No newline at end of file
diff --git a/features/adapter/validate.js b/test/cucumber/adapter/validate.steps.js
similarity index 83%
rename from features/adapter/validate.js
rename to test/cucumber/adapter/validate.steps.js
index db57e8e3cf9c7484445bbd05a71f9470269a2330..86596aa9dce908ee169b3917ead13ffa87c70952 100644
--- a/features/adapter/validate.js
+++ b/test/cucumber/adapter/validate.steps.js
@@ -1,7 +1,7 @@
 const assert = require('assert');
-const { Before, Given, When, Then } = require('@cucumber/cucumber');
+const { Given, When, Then } = require('@cucumber/cucumber');
 
-const { fixtures } = require('../../test/fixtures/server');
+const { fixtures } = require('../../fixtures/server');
 
 Given('a valid message', function() {
   this.message = fixtures.get('message-vehicle-status');
diff --git a/features/schema/validate.js b/test/cucumber/schema/validate.steps.js
similarity index 100%
rename from features/schema/validate.js
rename to test/cucumber/schema/validate.steps.js