Repository: serverless/serverless-azure-functions
Pull request #193 (2019-07-05 21:18:04)

refactor: add base plugin and rename plugins to follow convention
- [x]  All plugin classes now extends from base plugin
- [x] All plugin classes rename to follow naming convention

tbarlow12: How did coverage decrease 10%?
mydiemho: > How did coverage decrease 10%?

looks like a few plugins didn't have enough tests, and renaming them cause them to be counted
coveralls: 
[![Coverage Status](https://coveralls.io/builds/24466525/badge)](https://coveralls.io/builds/24466525)

Coverage decreased (-7.8%) to 76.321% when pulling **eeaf12248be8f2ca790f0739951027c781ab954b on myho/refactorBasePlugin** into **11c80b3a41986fb55191830e2c59141ce368ada5 on dev**.


Patch:
diff --git a/.gitignore b/.gitignore
index ee4336e..c93070f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@ node_modules/
 /lib/
 *.dll
 /coverage/
-.env*
\ No newline at end of file
+.env*
+.idea/
diff --git a/jest.config.js b/jest.config.js
index 6b84680..0e3d8c7 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -1,7 +1,8 @@
 module.exports = {
   "collectCoverageFrom": [
     "src/**/*.{js,jsx,ts,tsx}",
-    "!src/**/*.d.ts"
+    "!src/**/*.d.ts",
+    "!/src/test/"
   ],
   "testEnvironment": "node",
   "transform": {
diff --git a/package.json b/package.json
index c0e9b79..a6baa47 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,9 @@
   "repository": {
     "git": "https://github.com/serverless/serverless-azure-functions"
   },
+  "lcov.path": [
+    "./coverage/lcov.info"
+  ],
   "homepage": "https://github.com/serverless/serverless-azure-functions",
   "keywords": [
     "serverless",
diff --git a/src/index.ts b/src/index.ts
index 4ad846a..9d61b76 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -6,15 +6,15 @@ whole provider implementation.
 
 import Serverless from "serverless";
 import AzureProvider from "./provider/azureProvider";
-import { AzureInvoke } from "./plugins/invoke/azureInvoke";
-import { AzureLogs } from "./plugins/logs/azureLogs";
-import { AzureRemove } from "./plugins/remove/azureRemove";
-import { AzurePackage } from "./plugins/package/azurePackage";
+import { AzureInvokePlugin } from "./plugins/invoke/azureInvokePlugin";
+import { AzureLogsPlugin } from "./plugins/logs/azureLogsPlugin";
+import { AzureRemovePlugin } from "./plugins/remove/azureRemovePlugin";
+import { AzurePackagePlugin } from "./plugins/package/azurePackagePlugin";
 import { AzureDeployPlugin } from "./plugins/deploy/azureDeployPlugin";
-import { AzureLoginPlugin } from "./plugins/login/loginPlugin";
-import { AzureApimServicePlugin } from "./plugins/apim/apimServicePlugin";
-import { AzureApimFunctionPlugin } from "./plugins/apim/apimFunctionPlugin";
-import { AzureFuncPlugin } from "./plugins/func/azureFunc";
+import { AzureLoginPlugin } from "./plugins/login/azureLoginPlugin";
+import { AzureApimServicePlugin } from "./plugins/apim/azureApimServicePlugin";
+import { AzureApimFunctionPlugin } from "./plugins/apim/azureApimFunctionPlugin";
+import { AzureFuncPlugin } from "./plugins/func/azureFuncPlugin";
 import { AzureOfflinePlugin } from "./plugins/offline/azureOfflinePlugin"
 import { AzureRollbackPlugin } from "./plugins/rollback/azureRollbackPlugin"
 
@@ -24,10 +24,10 @@ export default class AzureIndex {
     this.serverless.setProvider(AzureProvider.getProviderName(), new AzureProvider(serverless) as any);
 
     // To be refactored
-    this.serverless.pluginManager.addPlugin(AzurePackage);
-    this.serverless.pluginManager.addPlugin(AzureInvoke);
-    this.serverless.pluginManager.addPlugin(AzureLogs);
-    this.serverless.pluginManager.addPlugin(AzureRemove);
+    this.serverless.pluginManager.addPlugin(AzurePackagePlugin);
+    this.serverless.pluginManager.addPlugin(AzureInvokePlugin);
+    this.serverless.pluginManager.addPlugin(AzureLogsPlugin);
+    this.serverless.pluginManager.addPlugin(AzureRemovePlugin);
     // Refactored
     this.serverless.pluginManager.addPlugin(AzureLoginPlugin);
     this.serverless.pluginManager.addPlugin(AzureDeployPlugin);
@@ -39,4 +39,4 @@ export default class AzureIndex {
   }
 }
 
-module.exports = AzureIndex;
\ No newline at end of file
+module.exports = AzureIndex;
diff --git a/src/plugins/apim/apimFunctionPlugin.ts b/src/plugins/apim/azureApimFunctionPlugin.ts
similarity index 67%
rename from src/plugins/apim/apimFunctionPlugin.ts
rename to src/plugins/apim/azureApimFunctionPlugin.ts
index 6427e60..da6cec6 100644
--- a/src/plugins/apim/apimFunctionPlugin.ts
+++ b/src/plugins/apim/azureApimFunctionPlugin.ts
@@ -1,10 +1,12 @@
 import Serverless from "serverless";
 import { ApimService } from "../../services/apimService";
+import { AzureBasePlugin } from "../azureBasePlugin";
 
-export class AzureApimFunctionPlugin {
+export class AzureApimFunctionPlugin extends AzureBasePlugin {
   public hooks: { [eventName: string]: Promise<any> };
 
-  public constructor(private serverless: Serverless, private options: Serverless.Options) {
+  public constructor(serverless: Serverless, private options: Serverless.Options) {
+    super(serverless);
     this.hooks = {
       "after:deploy:function:deploy": this.deploy.bind(this)
     };
@@ -19,6 +21,6 @@ export class AzureApimFunctionPlugin {
 
     await apimService.deployFunction(service, api, this.options);
 
-    this.serverless.cli.log("Finished APIM function deployment");
+    this.log("Finished APIM function deployment");
   }
-}
\ No newline at end of file
+}
diff --git a/src/plugins/apim/apimServicePlugin.ts b/src/plugins/apim/azureApimServicePlugin.ts
similarity index 71%
rename from src/plugins/apim/apimServicePlugin.ts
rename to src/plugins/apim/azureApimServicePlugin.ts
index 55c72e3..92d4739 100644
--- a/src/plugins/apim/apimServicePlugin.ts
+++ b/src/plugins/apim/azureApimServicePlugin.ts
@@ -1,10 +1,12 @@
 import Serverless from "serverless";
 import { ApimService } from "../../services/apimService";
+import { AzureBasePlugin } from "../azureBasePlugin";
 
-export class AzureApimServicePlugin {
+export class AzureApimServicePlugin extends AzureBasePlugin {
   public hooks: { [eventName: string]: Promise<any> };
 
-  public constructor(private serverless: Serverless, private options: Serverless.Options) {
+  public constructor(serverless: Serverless, private options: Serverless.Options) {
+    super(serverless);
     this.hooks = {
       "after:deploy:deploy": this.deploy.bind(this)
     };
@@ -23,6 +25,6 @@ export class AzureApimServicePlugin {
     const api = await apimService.deployApi();
     await apimService.deployFunctions(service, api);
 
-    this.serverless.cli.log("Finished APIM service deployment");
+    this.log("Finished APIM service deployment");
   }
-}
\ No newline at end of file
+}
diff --git a/src/plugins/azureBasePlugin.ts b/src/plugins/azureBasePlugin.ts
new file mode 100644
index 0000000..0869bad
--- /dev/null
+++ b/src/plugins/azureBasePlugin.ts
@@ -0,0 +1,14 @@
+import { Guard } from "../shared/guard";
+import Serverless from "serverless";
+
+export abstract class AzureBasePlugin {
+  public constructor(
+    protected serverless: Serverless,
+  ) {
+    Guard.null(serverless);
+  }
+
+  protected log(message: string) {
+    this.serverless.cli.log(message);
+  }
+}
diff --git a/src/plugins/deploy/azureDeployPlugin.ts b/src/plugins/deploy/azureDeployPlugin.ts
index 02ff649..52e451c 100644
--- a/src/plugins/deploy/azureDeployPlugin.ts
+++ b/src/plugins/deploy/azureDeployPlugin.ts
@@ -2,12 +2,15 @@ import Serverless from "serverless";
 import { FunctionAppService } from "../../services/functionAppService";
 import { ResourceService } from "../../services/resourceService";
 import { Utils } from "../../shared/utils";
+import { AzureBasePlugin } from "../azureBasePlugin";
 
-export class AzureDeployPlugin {
+export class AzureDeployPlugin extends AzureBasePlugin {
   public hooks: { [eventName: string]: Promise<any> };
   public commands: any;
 
-  public constructor(private serverless: Serverless, private options: Serverless.Options) {
+  public constructor(serverless: Serverless, private options: Serverless.Options) {
+    super(serverless);
+
     this.hooks = {
       "deploy:deploy": this.deploy.bind(this),
       "deploy:list:list": this.list.bind(this),
@@ -34,11 +37,11 @@ export class AzureDeployPlugin {
   }
 
   private async list() {
-    this.serverless.cli.log("Listing deployments");
+    this.log("Listing deployments");
     const resourceService = new ResourceService(this.serverless, this.options);
     const deployments = await resourceService.getDeployments();
     if (!deployments || deployments.length === 0) {
-      this.serverless.cli.log(`No deployments found for resource group '${resourceService.getResourceGroupName()}'`);
+      this.log(`No deployments found for resource group '${resourceService.getResourceGroupName()}'`);
       return;
     }
     let stringDeployments = "\n\nDeployments";
@@ -48,15 +51,13 @@ export class AzureDeployPlugin {
       stringDeployments += `Name: ${dep.name}\n`
       const timestampFromName = Utils.getTimestampFromName(dep.name);
       stringDeployments += `Timestamp: ${(timestampFromName) ? timestampFromName : "None"}\n`;
-      stringDeployments += `Datetime: ${(timestampFromName)
-        ?
-        new Date(+timestampFromName).toISOString()
-        :
-        "None"
-      }\n`
+
+      const dateTime = timestampFromName ? new Date(+timestampFromName).toISOString() : "None";
+      stringDeployments += `Datetime: ${dateTime}\n`
     }
+
     stringDeployments += "-----------\n"
-    this.serverless.cli.log(stringDeployments);
+    this.log(stringDeployments);
   }
 
   private async deploy() {
diff --git a/src/plugins/func/azureFunc.ts b/src/plugins/func/azureFuncPlugin.ts
similarity index 81%
rename from src/plugins/func/azureFunc.ts
rename to src/plugins/func/azureFuncPlugin.ts
index 09a8201..09ec9ad 100644
--- a/src/plugins/func/azureFunc.ts
+++ b/src/plugins/func/azureFuncPlugin.ts
@@ -1,13 +1,15 @@
 import Serverless from "serverless";
 import { FuncService } from "../../services/funcService";
+import { AzureBasePlugin } from "../azureBasePlugin";
 
-export class AzureFuncPlugin {
+export class AzureFuncPlugin extends AzureBasePlugin {
   public hooks: { [eventName: string]: Promise<any> };
   public commands: any;
   private service: FuncService;
 
+  public constructor(serverless: Serverless, private options: Serverless.Options) {
+    super(serverless);
 
-  public constructor(private serverless: Serverless, private options: Serverless.Options) {
     this.hooks = {
       "func:func": this.func.bind(this),
       "func:add:add": this.add.bind(this),
@@ -53,7 +55,7 @@ export class AzureFuncPlugin {
   }
 
   private async func() {
-    this.serverless.cli.log("Use the func plugin to add or remove functions within Function App");
+    this.log("Use the func plugin to add or remove functions within Function App");
   }
 
   private async add() {
@@ -63,4 +65,4 @@ export class AzureFuncPlugin {
   private async remove() {
     this.service.remove();
   }
-}
\ No newline at end of file
+}
diff --git a/src/plugins/invoke/azureInvoke.ts b/src/plugins/invoke/azureInvokePlugin.ts
similarity index 76%
rename from src/plugins/invoke/azureInvoke.ts
rename to src/plugins/invoke/azureInvokePlugin.ts
index f5fa576..0905530 100644
--- a/src/plugins/invoke/azureInvoke.ts
+++ b/src/plugins/invoke/azureInvokePlugin.ts
@@ -3,20 +3,22 @@ import Serverless from "serverless";
 import { InvokeService } from "../../services/invokeService";
 import fs from "fs";
 import { ServerlessCommandMap } from "../../models/serverless";
+import { AzureBasePlugin } from "../azureBasePlugin";
 
-export class AzureInvoke {
+export class AzureInvokePlugin extends AzureBasePlugin {
   public hooks: { [eventName: string]: Promise<any> };
   private commands: ServerlessCommandMap;
   private invokeService: InvokeService;
-  public constructor(private serverless: Serverless, private options: Serverless.Options) {
+  public constructor(serverless: Serverless, private options: Serverless.Options) {
+    super(serverless);
     const path = this.options["path"];
-    
+
     if (path) {
       const absolutePath = isAbsolute(path)
         ? path
         : join(this.serverless.config.servicePath, path);
-      this.serverless.cli.log(this.serverless.config.servicePath);
-      this.serverless.cli.log(path);
+      this.log(this.serverless.config.servicePath);
+      this.log(path);
 
       if (!fs.existsSync(absolutePath)) {
         throw new Error("The file you provided does not exist.");
@@ -53,20 +55,20 @@ export class AzureInvoke {
       "invoke:invoke": this.invoke.bind(this)
     };
   }
-  
+
   private async invoke() {
     const functionName = this.options["function"];
     const data = this.options["data"];
     const method = this.options["method"] || "GET";
     if (!functionName) {
-      this.serverless.cli.log("Need to provide a name of function to invoke");
+      this.log("Need to provide a name of function to invoke");
       return;
     }
 
     this.invokeService = new InvokeService(this.serverless, this.options);
-    const response =  await this.invokeService.invoke(method, functionName, data);
-    if(response){
-      this.serverless.cli.log(JSON.stringify(response.data));
+    const response = await this.invokeService.invoke(method, functionName, data);
+    if (response) {
+      this.log(JSON.stringify(response.data));
     }
   }
-}
\ No newline at end of file
+}
diff --git a/src/plugins/login/loginPlugin.ts b/src/plugins/login/azureLoginPlugin.ts
similarity index 79%
rename from src/plugins/login/loginPlugin.ts
rename to src/plugins/login/azureLoginPlugin.ts
index 6a1f488..d59b049 100644
--- a/src/plugins/login/loginPlugin.ts
+++ b/src/plugins/login/azureLoginPlugin.ts
@@ -1,12 +1,14 @@
 import Serverless from "serverless";
 import AzureProvider from "../../provider/azureProvider";
 import { AzureLoginService } from "../../services/loginService";
+import { AzureBasePlugin } from "../azureBasePlugin";
 
-export class AzureLoginPlugin {
+export class AzureLoginPlugin extends AzureBasePlugin {
   private provider: AzureProvider;
   public hooks: { [eventName: string]: Promise<any> };
 
-  public constructor(private serverless: Serverless, private options: Serverless.Options) {
+  public constructor(serverless: Serverless, private options: Serverless.Options) {
+    super(serverless);
     this.provider = (this.serverless.getProvider("azure") as any) as AzureProvider;
 
     this.hooks = {
@@ -23,7 +25,7 @@ export class AzureLoginPlugin {
       return;
     }
 
-    this.serverless.cli.log("Logging into Azure");
+    this.log("Logging into Azure");
 
     try {
       const authResult = await AzureLoginService.login();
@@ -33,9 +35,9 @@ export class AzureLoginPlugin {
       this.serverless.variables["subscriptionId"] = process.env.azureSubId || authResult.subscriptions[0].id;
     }
     catch (e) {
-      this.serverless.cli.log("Error logging into azure");
-      this.serverless.cli.log(`${e}`);
+      this.log("Error logging into azure");
+      this.log(`${e}`);
       process.exit(0);
     }
   }
-}
\ No newline at end of file
+}
diff --git a/src/plugins/logs/azureLogs.ts b/src/plugins/logs/azureLogsPlugin.ts
similarity index 70%
rename from src/plugins/logs/azureLogs.ts
rename to src/plugins/logs/azureLogsPlugin.ts
index 1a50f07..05e7c81 100644
--- a/src/plugins/logs/azureLogs.ts
+++ b/src/plugins/logs/azureLogsPlugin.ts
@@ -1,14 +1,17 @@
 import Serverless from "serverless";
 import AzureProvider from "../../provider/azureProvider";
+import { AzureBasePlugin } from "../azureBasePlugin";
 
-export class AzureLogs {
+export class AzureLogsPlugin extends AzureBasePlugin {
   public hooks: { [eventName: string]: Promise<any> };
 
   private provider: AzureProvider;
 
-  public constructor(private serverless: Serverless, private options: Serverless.Options) {
+  public constructor(serverless: Serverless, private options: Serverless.Options) {
+    super(serverless);
+
     this.provider = (this.serverless.getProvider("azure") as any) as AzureProvider;
-    
+
     this.hooks = {
       "logs:logs": this.retrieveLogs.bind(this)
     };
diff --git a/src/plugins/offline/azureOfflinePlugin.ts b/src/plugins/offline/azureOfflinePlugin.ts
index 0615edb..a3b1ad4 100644
--- a/src/plugins/offline/azureOfflinePlugin.ts
+++ b/src/plugins/offline/azureOfflinePlugin.ts
@@ -1,12 +1,14 @@
 import Serverless from "serverless";
 import { OfflineService } from "../../services/offlineService";
+import { AzureBasePlugin } from "../azureBasePlugin";
 
-export class AzureOfflinePlugin {
+export class AzureOfflinePlugin extends AzureBasePlugin {
   public hooks: { [eventName: string]: Promise<any> };
   public commands: any;
   private offlineService: OfflineService;
 
-  public constructor(private serverless: Serverless, private options: Serverless.Options) {
+  public constructor(serverless: Serverless, private options: Serverless.Options) {
+    super(serverless);
     this.offlineService = new OfflineService(this.serverless, this.options);
 
     this.hooks = {
diff --git a/src/plugins/package/azurePackage.ts b/src/plugins/package/azurePackagePlugin.ts
similarity index 86%
rename from src/plugins/package/azurePackage.ts
rename to src/plugins/package/azurePackagePlugin.ts
index 37b5881..ef317a3 100644
--- a/src/plugins/package/azurePackage.ts
+++ b/src/plugins/package/azurePackagePlugin.ts
@@ -2,14 +2,16 @@
 import Serverless from "serverless";
 import AzureProvider from "../../provider/azureProvider";
 import { PackageService } from "../../services/packageService";
+import { AzureBasePlugin } from "../azureBasePlugin";
 
-export class AzurePackage {
+export class AzurePackagePlugin extends AzureBasePlugin {
   private bindingsCreated: boolean = false;
   private packageService: PackageService;
   public provider: AzureProvider;
   public hooks: { [eventName: string]: Promise<any> };
 
-  public constructor(private serverless: Serverless) {
+  public constructor(serverless: Serverless) {
+    super(serverless);
     this.hooks = {
       "before:package:setupProviderConfiguration": this.setupProviderConfiguration.bind(this),
       "before:webpack:package:packageModules": this.webpack.bind(this),
diff --git a/src/plugins/remove/azureRemove.ts b/src/plugins/remove/azureRemovePlugin.ts
similarity index 62%
rename from src/plugins/remove/azureRemove.ts
rename to src/plugins/remove/azureRemovePlugin.ts
index 0ad85b2..4eb2928 100644
--- a/src/plugins/remove/azureRemove.ts
+++ b/src/plugins/remove/azureRemovePlugin.ts
@@ -1,10 +1,12 @@
 import Serverless from "serverless";
 import { ResourceService } from "../../services/resourceService";
+import { AzureBasePlugin } from "../azureBasePlugin";
 
-export class AzureRemove {
+export class AzureRemovePlugin extends AzureBasePlugin {
   public hooks: { [eventName: string]: Promise<any> };
 
-  public constructor(private serverless: Serverless, private options: Serverless.Options) {
+  public constructor(serverless: Serverless, private options: Serverless.Options) {
+    super(serverless);
     this.hooks = {
       "remove:remove": this.remove.bind(this)
     };
@@ -15,6 +17,6 @@ export class AzureRemove {
     await resourceClient.deleteDeployment();
     await resourceClient.deleteResourceGroup();
 
-    this.serverless.cli.log("Service successfully removed");
+    this.log("Service successfully removed");
   }
 }


Test patch:
diff --git a/src/index.test.ts b/src/index.test.ts
index 66f8d1d..5b9947b 100644
--- a/src/index.test.ts
+++ b/src/index.test.ts
@@ -1,13 +1,13 @@
 import AzureIndex from "./index";
 import { MockFactory } from "./test/mockFactory"
-import { AzureInvoke } from "./plugins/invoke/azureInvoke";
-import { AzureLogs } from "./plugins/logs/azureLogs";
-import { AzureRemove } from "./plugins/remove/azureRemove";
-import { AzurePackage } from "./plugins/package/azurePackage";
+import { AzureInvokePlugin } from "./plugins/invoke/azureInvokePlugin";
+import { AzureLogsPlugin } from "./plugins/logs/azureLogsPlugin";
+import { AzureRemovePlugin } from "./plugins/remove/azureRemovePlugin";
+import { AzurePackagePlugin } from "./plugins/package/azurePackagePlugin";
 import { AzureDeployPlugin } from "./plugins/deploy/azureDeployPlugin";
-import { AzureLoginPlugin } from "./plugins/login/loginPlugin";
-import { AzureApimServicePlugin } from "./plugins/apim/apimServicePlugin";
-import { AzureApimFunctionPlugin } from "./plugins/apim/apimFunctionPlugin";
+import { AzureLoginPlugin } from "./plugins/login/azureLoginPlugin";
+import { AzureApimServicePlugin } from "./plugins/apim/azureApimServicePlugin";
+import { AzureApimFunctionPlugin } from "./plugins/apim/azureApimFunctionPlugin";
 import AzureProvider from "./provider/azureProvider";
 
 describe("Azure Index", () => {
@@ -19,13 +19,13 @@ describe("Azure Index", () => {
 
     expect(sls.setProvider).toBeCalledWith("azure", new AzureProvider(sls));
 
-    expect(sls.pluginManager.addPlugin).toBeCalledWith(AzurePackage);
-    expect(sls.pluginManager.addPlugin).toBeCalledWith(AzureInvoke);
-    expect(sls.pluginManager.addPlugin).toBeCalledWith(AzureLogs);
-    expect(sls.pluginManager.addPlugin).toBeCalledWith(AzureRemove);
+    expect(sls.pluginManager.addPlugin).toBeCalledWith(AzurePackagePlugin);
+    expect(sls.pluginManager.addPlugin).toBeCalledWith(AzureInvokePlugin);
+    expect(sls.pluginManager.addPlugin).toBeCalledWith(AzureLogsPlugin);
+    expect(sls.pluginManager.addPlugin).toBeCalledWith(AzureRemovePlugin);
     expect(sls.pluginManager.addPlugin).toBeCalledWith(AzureLoginPlugin);
     expect(sls.pluginManager.addPlugin).toBeCalledWith(AzureDeployPlugin);
     expect(sls.pluginManager.addPlugin).toBeCalledWith(AzureApimServicePlugin);
     expect(sls.pluginManager.addPlugin).toBeCalledWith(AzureApimFunctionPlugin);
   });
-});
\ No newline at end of file
+});
diff --git a/src/plugins/apim/apimFunctionPlugin.test.ts b/src/plugins/apim/azureApimFunctionPlugin.test.ts
similarity index 92%
rename from src/plugins/apim/apimFunctionPlugin.test.ts
rename to src/plugins/apim/azureApimFunctionPlugin.test.ts
index a1c01b7..0e81528 100644
--- a/src/plugins/apim/apimFunctionPlugin.test.ts
+++ b/src/plugins/apim/azureApimFunctionPlugin.test.ts
@@ -1,6 +1,6 @@
 import { MockFactory } from "../../test/mockFactory";
 import { invokeHook } from "../../test/utils";
-import { AzureApimFunctionPlugin } from "./apimFunctionPlugin";
+import { AzureApimFunctionPlugin } from "./azureApimFunctionPlugin";
 
 jest.mock("../../services/apimService");
 import { ApimService } from "../../services/apimService";
@@ -22,4 +22,4 @@ describe("APIM Function Plugin", () => {
     expect(deployFunction).toBeCalled();
     expect(sls.cli.log).lastCalledWith("Finished APIM function deployment")
   });
-});
\ No newline at end of file
+});
diff --git a/src/plugins/apim/apimServicePlugin.test.ts b/src/plugins/apim/azureApimServicePlugin.test.ts
similarity index 96%
rename from src/plugins/apim/apimServicePlugin.test.ts
rename to src/plugins/apim/azureApimServicePlugin.test.ts
index e3434d3..aaff0fb 100644
--- a/src/plugins/apim/apimServicePlugin.test.ts
+++ b/src/plugins/apim/azureApimServicePlugin.test.ts
@@ -1,7 +1,7 @@
 import Serverless from "serverless";
 import { MockFactory } from "../../test/mockFactory";
 import { invokeHook } from "../../test/utils";
-import { AzureApimServicePlugin } from "./apimServicePlugin";
+import { AzureApimServicePlugin } from "./azureApimServicePlugin";
 
 jest.mock("../../services/apimService");
 import { ApimService } from "../../services/apimService";
@@ -59,4 +59,4 @@ describe("APIM Service Plugin", () => {
     expect(deployApi).not.toBeCalled();
     expect(deployFunctions).not.toBeCalled();
   });
-});
\ No newline at end of file
+});
diff --git a/src/plugins/azureBasePlugin.test.ts b/src/plugins/azureBasePlugin.test.ts
new file mode 100644
index 0000000..3269408
--- /dev/null
+++ b/src/plugins/azureBasePlugin.test.ts
@@ -0,0 +1,29 @@
+import { AzureBasePlugin } from "./azureBasePlugin";
+import Serverless from "serverless";
+import { MockFactory } from "../test/mockFactory";
+
+class MockPlugin extends AzureBasePlugin {
+  public constructor(serverless: Serverless) {
+    super(serverless);
+  }
+
+  public logMessage(message: string) {
+    this.log(message);
+  }
+}
+
+describe("Azure Base Plugin", () => {
+  let sls: Serverless;
+  let plugin: MockPlugin;
+
+  beforeEach(() => {
+    sls = MockFactory.createTestServerless();
+    plugin = new MockPlugin(sls);
+  });
+
+
+  it("logs a message", () => {
+    plugin.logMessage("test message");
+    expect(sls.cli.log).lastCalledWith("test message");
+  })
+})
diff --git a/src/plugins/deploy/azureDeployPlugin.test.ts b/src/plugins/deploy/azureDeployPlugin.test.ts
index 3813675..f54ed11 100644
--- a/src/plugins/deploy/azureDeployPlugin.test.ts
+++ b/src/plugins/deploy/azureDeployPlugin.test.ts
@@ -8,8 +8,21 @@ import { FunctionAppService } from "../../services/functionAppService";
 jest.mock("../../services/resourceService");
 import { ResourceService } from "../../services/resourceService";
 import { Site } from "@azure/arm-appservice/esm/models";
+import { ServerlessAzureOptions } from "../../models/serverless";
+import Serverless from "serverless";
 
 describe("Deploy plugin", () => {
+  let sls: Serverless;
+  let options: ServerlessAzureOptions;
+  let plugin: AzureDeployPlugin;
+
+  beforeEach(() => {
+    jest.resetAllMocks();
+    sls = MockFactory.createTestServerless();
+    options = MockFactory.createTestServerlessOptions();
+
+    plugin = new AzureDeployPlugin(sls, options);
+  });
 
   afterEach(() => {
     jest.resetAllMocks();
@@ -25,10 +38,6 @@ describe("Deploy plugin", () => {
     FunctionAppService.prototype.deploy = deploy;
     FunctionAppService.prototype.uploadFunctions = uploadFunctions;
 
-    const sls = MockFactory.createTestServerless();
-    const options = MockFactory.createTestServerlessOptions();
-    const plugin = new AzureDeployPlugin(sls, options);
-
     await invokeHook(plugin, "deploy:deploy");
 
     expect(deployResourceGroup).toBeCalled();
@@ -39,10 +48,10 @@ describe("Deploy plugin", () => {
   it("lists deployments with timestamps", async () => {
     const deployments = MockFactory.createTestDeployments(5, true);
     ResourceService.prototype.getDeployments = jest.fn(() => Promise.resolve(deployments));
-    const sls = MockFactory.createTestServerless();
-    const options = MockFactory.createTestServerlessOptions();
-    const plugin = new AzureDeployPlugin(sls, options);
+
     await invokeHook(plugin, "deploy:list:list");
+    expect(ResourceService.prototype.getDeployments).toBeCalled();
+
     let expectedLogStatement = "\n\nDeployments";
     const originalTimestamp = +MockFactory.createTestTimestamp();
     let i = 0
@@ -61,10 +70,10 @@ describe("Deploy plugin", () => {
   it("lists deployments without timestamps", async () => {
     const deployments = MockFactory.createTestDeployments();
     ResourceService.prototype.getDeployments = jest.fn(() => Promise.resolve(deployments));
-    const sls = MockFactory.createTestServerless();
-    const options = MockFactory.createTestServerlessOptions();
-    const plugin = new AzureDeployPlugin(sls, options);
+
     await invokeHook(plugin, "deploy:list:list");
+    expect(ResourceService.prototype.getDeployments).toBeCalled();
+
     let expectedLogStatement = "\n\nDeployments";
     for (const dep of deployments) {
       expectedLogStatement += "\n-----------\n"
@@ -77,13 +86,13 @@ describe("Deploy plugin", () => {
   });
 
   it("logs empty deployment list", async () => {
-    const sls = MockFactory.createTestServerless();
     const resourceGroup = "rg1";
     ResourceService.prototype.getDeployments = jest.fn(() => Promise.resolve([])) as any;
     ResourceService.prototype.getResourceGroupName = jest.fn(() => resourceGroup);
-    const options = MockFactory.createTestServerlessOptions();
-    const plugin = new AzureDeployPlugin(sls, options);
+
     await invokeHook(plugin, "deploy:list:list");
+    expect(ResourceService.prototype.getDeployments).toBeCalled();
+
     expect(sls.cli.log).lastCalledWith(`No deployments found for resource group '${resourceGroup}'`);
   });
 });
diff --git a/src/plugins/func/azureFunc.test.ts b/src/plugins/func/azureFuncPlugin.test.ts
similarity index 98%
rename from src/plugins/func/azureFunc.test.ts
rename to src/plugins/func/azureFuncPlugin.test.ts
index 9c490c1..57db0e8 100644
--- a/src/plugins/func/azureFunc.test.ts
+++ b/src/plugins/func/azureFuncPlugin.test.ts
@@ -3,7 +3,7 @@ import mockFs from "mock-fs";
 import rimraf from "rimraf";
 import { MockFactory } from "../../test/mockFactory";
 import { invokeHook } from "../../test/utils";
-import { AzureFuncPlugin } from "./azureFunc";
+import { AzureFuncPlugin } from "./azureFuncPlugin";
 
 describe("Azure Func Plugin", () => {
 
@@ -134,4 +134,4 @@ describe("Azure Func Plugin", () => {
       expect(sls.utils.writeFileSync).toBeCalledWith("serverless.yml", MockFactory.createTestServerlessYml(true, expectedFunctionsYml))
     });
   });
-});
\ No newline at end of file
+});
diff --git a/src/plugins/invoke/azureInvoke.test.ts b/src/plugins/invoke/azureInvokePlugin.test.ts
similarity index 87%
rename from src/plugins/invoke/azureInvoke.test.ts
rename to src/plugins/invoke/azureInvokePlugin.test.ts
index 982f562..e4710d4 100644
--- a/src/plugins/invoke/azureInvoke.test.ts
+++ b/src/plugins/invoke/azureInvokePlugin.test.ts
@@ -1,14 +1,14 @@
 import { MockFactory } from "../../test/mockFactory";
 import { invokeHook } from "../../test/utils";
 import mockFs from "mock-fs";
-import { AzureInvoke } from "./azureInvoke";
+import { AzureInvokePlugin } from "./azureInvokePlugin";
 jest.mock("../../services/functionAppService");
 jest.mock("../../services/resourceService");
 jest.mock("../../services/invokeService");
 import { InvokeService } from "../../services/invokeService";
 
 describe("Azure Invoke Plugin", () => {
-  const fileContent =  JSON.stringify({
+  const fileContent = JSON.stringify({
     name: "Azure-Test",
   });
   afterEach(() => {
@@ -35,7 +35,7 @@ describe("Azure Invoke Plugin", () => {
     options["data"] = "{\"name\": \"AzureTest\"}";
     options["method"] = "GET";
 
-    const plugin = new AzureInvoke(sls, options);
+    const plugin = new AzureInvokePlugin(sls, options);
     await invokeHook(plugin, "invoke:invoke");
     expect(invoke).toBeCalledWith(options["method"], options["function"], options["data"]);
     expect(sls.cli.log).toBeCalledWith(JSON.stringify(expectedResult.data));
@@ -50,11 +50,11 @@ describe("Azure Invoke Plugin", () => {
     options["function"] = "testApp";
     options["path"] = "testFile.json";
     options["method"] = "GET";
-    const plugin = new AzureInvoke(sls, options);
+    const plugin = new AzureInvokePlugin(sls, options);
     await invokeHook(plugin, "invoke:invoke");
     expect(invoke).toBeCalledWith(options["method"], options["function"], fileContent);
     expect(sls.cli.log).toBeCalledWith(JSON.stringify(expectedResult.data));
-  
+
   });
 
   it("calls the invoke hook with file path", async () => {
@@ -65,9 +65,9 @@ describe("Azure Invoke Plugin", () => {
     options["function"] = "testApp";
     options["path"] = "notExist.json";
     options["method"] = "GET";
-    expect(() => new AzureInvoke(sls, options)).toThrow();
+    expect(() => new AzureInvokePlugin(sls, options)).toThrow();
   });
-  
+
   it("Function invoked with no data", async () => {
     const expectedResult = { data: "test" };
     const invoke = jest.fn(() => expectedResult);
@@ -76,8 +76,8 @@ describe("Azure Invoke Plugin", () => {
     const options = MockFactory.createTestServerlessOptions();
     options["function"] = "testApp";
     options["method"] = "GET";
-    const plugin = new AzureInvoke(sls, options);
-    await invokeHook(plugin, "invoke:invoke"); 
+    const plugin = new AzureInvokePlugin(sls, options);
+    await invokeHook(plugin, "invoke:invoke");
     expect(invoke).toBeCalledWith(options["method"], options["function"], undefined);
     expect(sls.cli.log).toBeCalledWith(JSON.stringify(expectedResult.data));
   });
@@ -90,8 +90,8 @@ describe("Azure Invoke Plugin", () => {
     options["function"] = null;
     options["data"] = "{\"name\": \"AzureTest\"}";
     options["method"] = "GET";
-    const plugin = new AzureInvoke(sls, options);
-    await invokeHook(plugin, "invoke:invoke"); 
+    const plugin = new AzureInvokePlugin(sls, options);
+    await invokeHook(plugin, "invoke:invoke");
     expect(invoke).not.toBeCalled();
   });
 });
diff --git a/src/plugins/login/loginPlugin.test.ts b/src/plugins/login/azureLoginPlugin.test.ts
similarity index 97%
rename from src/plugins/login/loginPlugin.test.ts
rename to src/plugins/login/azureLoginPlugin.test.ts
index 0620e6f..aef2f2d 100644
--- a/src/plugins/login/loginPlugin.test.ts
+++ b/src/plugins/login/azureLoginPlugin.test.ts
@@ -2,7 +2,7 @@ import Serverless from "serverless";
 import { AzureLoginService } from "../../services/loginService";
 import { MockFactory } from "../../test/mockFactory";
 import { invokeHook, setEnvVariables, unsetEnvVariables } from "../../test/utils";
-import { AzureLoginPlugin } from "./loginPlugin";
+import { AzureLoginPlugin } from "./azureLoginPlugin";
 
 describe("Login Plugin", () => {
 
@@ -54,7 +54,7 @@ describe("Login Plugin", () => {
   });
 
   it("calls service principal login if environment variables are set", async () => {
-    setServicePrincipalEnvVariables();    
+    setServicePrincipalEnvVariables();
     const sls = MockFactory.createTestServerless();
     await invokeLoginHook(false, sls);
     expect(AzureLoginService.servicePrincipalLogin).toBeCalledWith(
diff --git a/src/plugins/offline/azureOfflinePlugin.test.ts b/src/plugins/offline/azureOfflinePlugin.test.ts
index d8a6cda..c6cbbcc 100644
--- a/src/plugins/offline/azureOfflinePlugin.test.ts
+++ b/src/plugins/offline/azureOfflinePlugin.test.ts
@@ -1,6 +1,3 @@
-import fs from "fs";
-import mockFs from "mock-fs";
-import path from "path";
 import Serverless from "serverless";
 import { MockFactory } from "../../test/mockFactory";
 import { invokeHook } from "../../test/utils";
@@ -9,7 +6,9 @@ import { AzureOfflinePlugin } from "./azureOfflinePlugin";
 jest.mock("../../services/offlineService")
 import { OfflineService } from "../../services/offlineService"
 
+
 describe("Azure Offline Plugin", () => {
+  let plugin: AzureOfflinePlugin;
 
   function createPlugin(sls?: Serverless, options?: Serverless.Options) {
     return new AzureOfflinePlugin(
@@ -17,24 +16,31 @@ describe("Azure Offline Plugin", () => {
       options || MockFactory.createTestServerlessOptions(),
     )
   }
+  beforeEach(() => {
+    jest.resetAllMocks();
+    plugin = createPlugin();
+  });
+
+  it("can be instantiated", () => {
+    expect(plugin).not.toBeNull();
+  });
 
-  it("invokes build hook", async () => {
-    const sls = MockFactory.createTestServerless();
-    const plugin = createPlugin(sls);
+  it("create function bindings when calling build", async () => {
     await invokeHook(plugin, "offline:build:build");
     expect(OfflineService.prototype.build).toBeCalled();
   });
 
-  it("invokes offline hook", async () => {
-    const sls = MockFactory.createTestServerless();
-    const plugin = createPlugin(sls);
+  it("create function bindings before offline:offline lifecyle event", async () => {
+    await invokeHook(plugin, "before:offline:offline");
+    expect(OfflineService.prototype.build).toBeCalled();
+  });
+
+  it("call offline service's start method when invoking offline command ", async () => {
     await invokeHook(plugin, "offline:offline");
     expect(OfflineService.prototype.start).toBeCalled();
   });
 
-  it("invokes cleanup hook", async () => {
-    const sls = MockFactory.createTestServerless();
-    const plugin = createPlugin(sls);
+  it("clean up files created for offline development when invoking cleanup command", async () => {
     await invokeHook(plugin, "offline:cleanup:cleanup");
     expect(OfflineService.prototype.cleanup).toBeCalled();
   });
diff --git a/src/plugins/package/azurePackage.test.ts b/src/plugins/package/azurePackagePlugin.test.ts
similarity index 92%
rename from src/plugins/package/azurePackage.test.ts
rename to src/plugins/package/azurePackagePlugin.test.ts
index 398a24d..34ddcd5 100644
--- a/src/plugins/package/azurePackage.test.ts
+++ b/src/plugins/package/azurePackagePlugin.test.ts
@@ -1,19 +1,19 @@
 import Serverless from "serverless";
 import { MockFactory } from "../../test/mockFactory";
 import { invokeHook } from "../../test/utils";
-import { AzurePackage } from "./azurePackage";
+import { AzurePackagePlugin } from "./azurePackagePlugin";
 jest.mock("../../services/packageService");
 import { PackageService } from "../../services/packageService";
 
 describe("Azure Package Plugin", () => {
   let sls: Serverless;
-  let plugin: AzurePackage;
+  let plugin: AzurePackagePlugin;
 
   beforeEach(() => {
     jest.resetAllMocks();
     sls = MockFactory.createTestServerless();
 
-    plugin = new AzurePackage(sls);
+    plugin = new AzurePackagePlugin(sls);
   });
 
   it("sets creates function bindings before package:setupProviderConfiguration life cycle event", async () => {
diff --git a/src/plugins/remove/azureRemove.test.ts b/src/plugins/remove/azureRemovePlugin.test.ts
similarity index 87%
rename from src/plugins/remove/azureRemove.test.ts
rename to src/plugins/remove/azureRemovePlugin.test.ts
index 23d5eb3..b89f001 100644
--- a/src/plugins/remove/azureRemove.test.ts
+++ b/src/plugins/remove/azureRemovePlugin.test.ts
@@ -1,6 +1,6 @@
 import { MockFactory } from "../../test/mockFactory";
 import { invokeHook } from "../../test/utils";
-import { AzureRemove } from "./azureRemove";
+import { AzureRemovePlugin } from "./azureRemovePlugin";
 
 jest.mock("../../services/resourceService");
 import { ResourceService } from "../../services/resourceService";
@@ -15,7 +15,7 @@ describe("Remove Plugin", () => {
 
     const sls = MockFactory.createTestServerless();
     const options = MockFactory.createTestServerlessOptions();
-    const plugin = new AzureRemove(sls, options);
+    const plugin = new AzureRemovePlugin(sls, options);
 
     await invokeHook(plugin, "remove:remove");
 
@@ -23,4 +23,4 @@ describe("Remove Plugin", () => {
     expect(deleteResourceGroup).toBeCalled();
     expect(sls.cli.log).toBeCalledWith("Service successfully removed");
   });
-});
\ No newline at end of file
+});