diff --git a/samcli/commands/local/lib/local_api_service.py b/samcli/commands/local/lib/local_api_service.py
index e8761798dec..e0243192c8c 100644
--- a/samcli/commands/local/lib/local_api_service.py
+++ b/samcli/commands/local/lib/local_api_service.py
@@ -5,7 +5,7 @@
 import os
 import logging
 
-from samcli.local.apigw.service import Service, Route
+from samcli.local.apigw.local_apigw_service import LocalApigwService, Route
 from samcli.commands.local.lib.sam_api_provider import SamApiProvider
 from samcli.commands.local.lib.exceptions import NoApisDefined
 
@@ -62,12 +62,12 @@ def start(self):
         # contains the response to the API which is sent out as HTTP response. Only stderr needs to be printed
         # to the console or a log file. stderr from Docker container contains runtime logs and output of print
         # statements from the Lambda function
-        service = Service(routing_list=routing_list,
-                          lambda_runner=self.lambda_runner,
-                          static_dir=static_dir_path,
-                          port=self.port,
-                          host=self.host,
-                          stderr=self.stderr_stream)
+        service = LocalApigwService(routing_list=routing_list,
+                                    lambda_runner=self.lambda_runner,
+                                    static_dir=static_dir_path,
+                                    port=self.port,
+                                    host=self.host,
+                                    stderr=self.stderr_stream)
 
         service.create()
 
diff --git a/samcli/local/apigw/service.py b/samcli/local/apigw/local_apigw_service.py
similarity index 77%
rename from samcli/local/apigw/service.py
rename to samcli/local/apigw/local_apigw_service.py
index 6441708367e..ec4a01c942a 100644
--- a/samcli/local/apigw/service.py
+++ b/samcli/local/apigw/local_apigw_service.py
@@ -2,11 +2,11 @@
 import io
 import json
 import logging
-import os
 import base64
 
-from flask import Flask, request, Response
+from flask import Flask, request
 
+from samcli.local.services.base_local_service import BaseLocalService, LambdaOutputParser
 from samcli.local.lambdafn.exceptions import FunctionNotFound
 from samcli.local.events.api_event import ContextIdentity, RequestContext, ApiGatewayLambdaEvent
 from .service_error_responses import ServiceErrorResponses
@@ -48,7 +48,7 @@ def __init__(self, methods, function_name, path, binary_types=None):
         self.binary_types = binary_types or []
 
 
-class Service(object):
+class LocalApigwService(BaseLocalService):
 
     _DEFAULT_PORT = 3000
     _DEFAULT_HOST = '127.0.0.1'
@@ -65,16 +65,14 @@ def __init__(self, routing_list, lambda_runner, static_dir=None, port=None, host
         :param int port: Optional. port for the service to start listening on
           Defaults to 3000
         :param str host: Optional. host to start the service on
-          Defaults to '0.0.0.0'
+          Defaults to '127.0.0.1
         :param io.BaseIO stderr: Optional stream where the stderr from Docker container should be written to
         """
+        super(LocalApigwService, self).__init__(lambda_runner.is_debugging(), port=port, host=host)
         self.routing_list = routing_list
         self.lambda_runner = lambda_runner
         self.static_dir = static_dir
-        self.port = port or self._DEFAULT_PORT
-        self.host = host or self._DEFAULT_HOST
         self._dict_of_routes = {}
-        self._app = None
         self.stderr = stderr
 
     def create(self):
@@ -128,32 +126,6 @@ def _construct_error_handling(self):
         # Something went wrong
         self._app.register_error_handler(500, ServiceErrorResponses.lambda_failure_response)
 
-    def run(self):
-        """
-        This starts up the (threaded) Local Server.
-        Note: This is a **blocking call**
-
-        :raise RuntimeError: If the service was not created
-        """
-
-        if not self._app:
-            raise RuntimeError("The application must be created before running")
-
-        # Flask can operate as a single threaded server (which is default) and a multi-threaded server which is
-        # more for development. When the Lambda container is going to be debugged, then it does not make sense
-        # to turn on multi-threading because customers can realistically attach only one container at a time to
-        # the debugger. Keeping this single threaded also enables the Lambda Runner to handle Ctrl+C in order to
-        # kill the container gracefully (Ctrl+C can be handled only by the main thread)
-        multi_threaded = not self.lambda_runner.is_debugging()
-
-        LOG.debug("Local API Server starting up. Multi-threading = %s", multi_threaded)
-
-        # This environ signifies we are running a main function for Flask. This is true, since we are using it within
-        # our cli and not on a production server.
-        os.environ['WERKZEUG_RUN_MAIN'] = 'true'
-
-        self._app.run(threaded=multi_threaded, host=self.host, port=self.port)
-
     def _request_handler(self, **kwargs):
         """
         We handle all requests to the host:port. The general flow of handling a request is as follows
@@ -186,7 +158,7 @@ def _request_handler(self, **kwargs):
         except FunctionNotFound:
             return ServiceErrorResponses.lambda_not_found_response()
 
-        lambda_response, lambda_logs = self._get_lambda_output(stdout_stream)
+        lambda_response, lambda_logs = LambdaOutputParser.get_lambda_output(stdout_stream)
 
         if self.stderr and lambda_logs:
             # Write the logs to stderr if available.
@@ -203,21 +175,6 @@ def _request_handler(self, **kwargs):
 
         return self._service_response(body, headers, status_code)
 
-    @staticmethod
-    def _service_response(body, headers, status_code):
-        """
-        Constructs a Flask Response from the body, headers, and status_code.
-
-        :param str body: Response body as a string
-        :param dict headers: headers for the response
-        :param int status_code: status_code for response
-        :return: Flask Response
-        """
-        response = Response(body)
-        response.headers = headers
-        response.status_code = status_code
-        return response
-
     def _get_current_route(self, flask_request):
         """
         Get the route (Route) based on the current request
@@ -239,46 +196,6 @@ def _get_current_route(self, flask_request):
 
         return route
 
-    @staticmethod
-    def _get_lambda_output(stdout_stream):
-        """
-        This method will extract read the given stream and return the response from Lambda function separated out
-        from any log statements it might have outputted. Logs end up in the stdout stream if the Lambda function
-        wrote directly to stdout using System.out.println or equivalents.
-
-        Parameters
-        ----------
-        stdout_stream : io.BaseIO
-            Stream to fetch data from
-
-        Returns
-        -------
-        str
-            String data containing response from Lambda function
-        str
-            String data containng logs statements, if any.
-        """
-        # We only want the last line of stdout, because it's possible that
-        # the function may have written directly to stdout using
-        # System.out.println or similar, before docker-lambda output the result
-        stdout_data = stdout_stream.getvalue().rstrip(b'\n')
-
-        # Usually the output is just one line and contains response as JSON string, but if the Lambda function
-        # wrote anything directly to stdout, there will be additional lines. So just extract the last line as
-        # response and everything else as log output.
-        lambda_response = stdout_data
-        lambda_logs = None
-
-        last_line_position = stdout_data.rfind(b'\n')
-        if last_line_position > 0:
-            # So there are multiple lines. Separate them out.
-            # Everything but the last line are logs
-            lambda_logs = stdout_data[:last_line_position]
-            # Last line is Lambda response. Make sure to strip() so we get rid of extra whitespaces & newlines around
-            lambda_response = stdout_data[last_line_position:].strip()
-
-        return lambda_response, lambda_logs
-
     # Consider moving this out to its own class. Logic is started to get dense and looks messy @jfuss
     @staticmethod
     def _parse_lambda_output(lambda_output, binary_types, flask_request):
@@ -308,7 +225,7 @@ def _parse_lambda_output(lambda_output, binary_types, flask_request):
             LOG.info("No Content-Type given. Defaulting to 'application/json'.")
             headers["Content-Type"] = "application/json"
 
-        if Service._should_base64_decode_body(binary_types, flask_request, headers, is_base_64_encoded):
+        if LocalApigwService._should_base64_decode_body(binary_types, flask_request, headers, is_base_64_encoded):
             body = base64.b64decode(body)
 
         return status_code, headers, body
@@ -357,7 +274,7 @@ def _construct_event(flask_request, port, binary_types):
 
         request_mimetype = flask_request.mimetype
 
-        is_base_64 = Service._should_base64_encode(binary_types, request_mimetype)
+        is_base_64 = LocalApigwService._should_base64_encode(binary_types, request_mimetype)
 
         if is_base_64:
             LOG.debug("Incoming Request seems to be binary. Base64 encoding the request data before sending to Lambda.")
@@ -380,7 +297,7 @@ def _construct_event(flask_request, port, binary_types):
         # APIGW does not support duplicate query parameters. Flask gives query params as a list so
         # we need to convert only grab the first item unless many were given, were we grab the last to be consistent
         # with APIGW
-        query_string_dict = Service._query_string_params(flask_request)
+        query_string_dict = LocalApigwService._query_string_params(flask_request)
 
         event = ApiGatewayLambdaEvent(http_method=method,
                                       body=request_data,
diff --git a/samcli/local/lambda_service/__init__.py b/samcli/local/lambda_service/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/samcli/local/lambda_service/local_lambda_invoke_service.py b/samcli/local/lambda_service/local_lambda_invoke_service.py
new file mode 100644
index 00000000000..4b65ec60e76
--- /dev/null
+++ b/samcli/local/lambda_service/local_lambda_invoke_service.py
@@ -0,0 +1,90 @@
+"""Local Lambda Service that only invokes a function"""
+
+import logging
+import io
+
+from flask import Flask, request
+
+
+from samcli.local.services.base_local_service import BaseLocalService, LambdaOutputParser
+from samcli.local.lambdafn.exceptions import FunctionNotFound
+
+LOG = logging.getLogger(__name__)
+
+
+class LocalLambdaInvokeService(BaseLocalService):
+
+    def __init__(self, lambda_runner, port, host, stderr=None):
+        """
+        Creates a Local Lambda Service that will only response to invoking a function
+
+        Parameters
+        ----------
+        lambda_runner samcli.commands.local.lib.local_lambda.LocalLambdaRunner
+            The Lambda runner class capable of invoking the function
+        port int
+            Optional. port for the service to start listening on
+        host str
+            Optional. host to start the service on
+        stderr io.BaseIO
+            Optional stream where the stderr from Docker container should be written to
+        """
+        super(LocalLambdaInvokeService, self).__init__(lambda_runner.is_debugging(), port=port, host=host)
+        self.lambda_runner = lambda_runner
+        self.stderr = stderr
+
+    def create(self):
+        """
+        Creates a Flask Application that can be started.
+        """
+        self._app = Flask(__name__)
+
+        path = '/2015-03-31/functions/<function_name>/invocations'
+        self._app.add_url_rule(path,
+                               endpoint=path,
+                               view_func=self._invoke_request_handler,
+                               methods=['POST'],
+                               provide_automatic_options=False)
+
+        self._construct_error_handling()
+
+    def _construct_error_handling(self):
+        """
+        Updates the Flask app with Error Handlers for different Error Codes
+
+        """
+        pass
+
+    def _invoke_request_handler(self, function_name):
+        """
+        Request Handler for the Local Lambda Invoke path. This method is responsible for understanding the incoming
+        request and invoking the Local Lambda Function
+
+        Parameters
+        ----------
+        function_name str
+            Name of the function to invoke
+
+        Returns
+        -------
+        A Flask Response response object as if it was returned from Lambda
+
+        """
+        flask_request = request
+        request_data = flask_request.get_data().decode('utf-8')
+
+        stdout_stream = io.BytesIO()
+
+        try:
+            self.lambda_runner.invoke(function_name, request_data, stdout=stdout_stream, stderr=self.stderr)
+        except FunctionNotFound:
+            # TODO Change this
+            raise Exception('Change this later')
+
+        lambda_response, lambda_logs = LambdaOutputParser.get_lambda_output(stdout_stream)
+
+        if self.stderr and lambda_logs:
+            # Write the logs to stderr if available.
+            self.stderr.write(lambda_logs)
+
+        return self._service_response(lambda_response, {'Content-Type': 'application/json'}, 200)
diff --git a/samcli/local/services/__init__.py b/samcli/local/services/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/samcli/local/services/base_local_service.py b/samcli/local/services/base_local_service.py
new file mode 100644
index 00000000000..9a2874bc7f2
--- /dev/null
+++ b/samcli/local/services/base_local_service.py
@@ -0,0 +1,121 @@
+"""Base class for all Services that interact with Local Lambda"""
+
+import logging
+import os
+
+from flask import Response
+
+LOG = logging.getLogger(__name__)
+
+
+class BaseLocalService(object):
+
+    def __init__(self, is_debugging, port, host):
+        """
+        Creates a BaseLocalService class
+
+        Parameters
+        ----------
+        is_debugging bool
+            Flag to run in debug mode or not
+        port int
+            Optional. port for the service to start listening on Defaults to 3000
+        host str
+            Optional. host to start the service on Defaults to '127.0.0.1
+        """
+        self.is_debugging = is_debugging
+        self.port = port
+        self.host = host
+        self._app = None
+
+    def create(self):
+        """
+        Creates a Flask Application that can be started.
+        """
+        raise NotImplementedError("Required method to implement")
+
+    def run(self):
+        """
+        This starts up the (threaded) Local Server.
+        Note: This is a **blocking call**
+
+        Raises
+        ------
+        RuntimeError
+            if the service was not created
+        """
+        if not self._app:
+            raise RuntimeError("The application must be created before running")
+
+        # Flask can operate as a single threaded server (which is default) and a multi-threaded server which is
+        # more for development. When the Lambda container is going to be debugged, then it does not make sense
+        # to turn on multi-threading because customers can realistically attach only one container at a time to
+        # the debugger. Keeping this single threaded also enables the Lambda Runner to handle Ctrl+C in order to
+        # kill the container gracefully (Ctrl+C can be handled only by the main thread)
+        multi_threaded = not self.is_debugging
+
+        LOG.debug("Localhost server is starting up. Multi-threading = %s", multi_threaded)
+
+        # This environ signifies we are running a main function for Flask. This is true, since we are using it within
+        # our cli and not on a production server.
+        os.environ['WERKZEUG_RUN_MAIN'] = 'true'
+
+        self._app.run(threaded=multi_threaded, host=self.host, port=self.port)
+
+    @staticmethod
+    def _service_response(body, headers, status_code):
+        """
+        Constructs a Flask Response from the body, headers, and status_code.
+
+        :param str body: Response body as a string
+        :param dict headers: headers for the response
+        :param int status_code: status_code for response
+        :return: Flask Response
+        """
+        response = Response(body)
+        response.headers = headers
+        response.status_code = status_code
+        return response
+
+
+class LambdaOutputParser(object):
+
+    @staticmethod
+    def get_lambda_output(stdout_stream):
+        """
+        This method will extract read the given stream and return the response from Lambda function separated out
+        from any log statements it might have outputted. Logs end up in the stdout stream if the Lambda function
+        wrote directly to stdout using System.out.println or equivalents.
+
+        Parameters
+        ----------
+        stdout_stream : io.BaseIO
+            Stream to fetch data from
+
+        Returns
+        -------
+        str
+            String data containing response from Lambda function
+        str
+            String data containng logs statements, if any.
+        """
+        # We only want the last line of stdout, because it's possible that
+        # the function may have written directly to stdout using
+        # System.out.println or similar, before docker-lambda output the result
+        stdout_data = stdout_stream.getvalue().rstrip(b'\n')
+
+        # Usually the output is just one line and contains response as JSON string, but if the Lambda function
+        # wrote anything directly to stdout, there will be additional lines. So just extract the last line as
+        # response and everything else as log output.
+        lambda_response = stdout_data
+        lambda_logs = None
+
+        last_line_position = stdout_data.rfind(b'\n')
+        if last_line_position > 0:
+            # So there are multiple lines. Separate them out.
+            # Everything but the last line are logs
+            lambda_logs = stdout_data[:last_line_position]
+            # Last line is Lambda response. Make sure to strip() so we get rid of extra whitespaces & newlines around
+            lambda_response = stdout_data[last_line_position:].strip()
+
+        return lambda_response, lambda_logs
diff --git a/tests/functional/local/apigw/test_service.py b/tests/functional/local/apigw/test_local_apigw_service.py
similarity index 99%
rename from tests/functional/local/apigw/test_service.py
rename to tests/functional/local/apigw/test_local_apigw_service.py
index 8cf6b851d60..7ae8fa89399 100644
--- a/tests/functional/local/apigw/test_service.py
+++ b/tests/functional/local/apigw/test_local_apigw_service.py
@@ -4,13 +4,12 @@
 import shutil
 import json
 import time
-import logging
 
 import requests
 import random
 from mock import Mock
 
-from samcli.local.apigw.service import Route, Service
+from samcli.local.apigw.local_apigw_service import Route, LocalApigwService
 from tests.functional.function_code import nodejs_lambda, API_GATEWAY_ECHO_EVENT, API_GATEWAY_BAD_PROXY_RESPONSE, API_GATEWAY_ECHO_BASE64_EVENT, API_GATEWAY_CONTENT_TYPE_LOWER
 from samcli.commands.local.lib import provider
 from samcli.local.lambdafn.runtime import LambdaRuntime
@@ -550,6 +549,7 @@ def test_post_binary_with_incorrect_content_type(self):
         self.assertEquals(response.status_code, 502)
         self.assertEquals(response.headers.get('Content-Type'), "application/json")
 
+
 class TestService_FlaskDefaultOptionsDisabled(TestCase):
     @classmethod
     def setUpClass(cls):
@@ -617,7 +617,7 @@ def make_service(list_of_routes, function_provider, cwd):
                                       function_provider=function_provider,
                                       cwd=cwd)
 
-    service = Service(list_of_routes, lambda_runner, port=port)
+    service = LocalApigwService(list_of_routes, lambda_runner, port=port)
 
     scheme = "http"
     url = '{}://0.0.0.0:{}'.format(scheme, port)
diff --git a/tests/functional/local/lambda_service/__init__.py b/tests/functional/local/lambda_service/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/functional/local/lambda_service/test_local_lambda_invoke.py b/tests/functional/local/lambda_service/test_local_lambda_invoke.py
new file mode 100644
index 00000000000..e6dc0e82772
--- /dev/null
+++ b/tests/functional/local/lambda_service/test_local_lambda_invoke.py
@@ -0,0 +1,126 @@
+import threading
+import shutil
+import random
+from mock import Mock
+import time
+from unittest import TestCase
+import os
+
+import requests
+
+from samcli.local.lambda_service.local_lambda_invoke_service import LocalLambdaInvokeService
+from tests.functional.function_code import nodejs_lambda, HELLO_FROM_LAMBDA, ECHO_CODE
+from samcli.commands.local.lib import provider
+from samcli.local.lambdafn.runtime import LambdaRuntime
+from samcli.commands.local.lib.local_lambda import LocalLambdaRunner
+from samcli.local.docker.manager import ContainerManager
+
+
+class TestLocalLambdaService(TestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.code_abs_path = nodejs_lambda(HELLO_FROM_LAMBDA)
+
+        # Let's convert this absolute path to relative path. Let the parent be the CWD, and codeuri be the folder
+        cls.cwd = os.path.dirname(cls.code_abs_path)
+        cls.code_uri = os.path.relpath(cls.code_abs_path, cls.cwd)  # Get relative path with respect to CWD
+
+        cls.function_name = "HelloWorld"
+
+        cls.function = provider.Function(name=cls.function_name, runtime="nodejs4.3", memory=256, timeout=5,
+                                         handler="index.handler", codeuri=cls.code_uri, environment=None,
+                                         rolearn=None)
+
+        cls.mock_function_provider = Mock()
+        cls.mock_function_provider.get.return_value = cls.function
+
+        list_of_function_names = ['HelloWorld']
+
+        cls.service, cls.port, cls.url, cls.scheme = make_service(list_of_function_names, cls.mock_function_provider, cls.cwd)
+        cls.service.create()
+        t = threading.Thread(name='thread', target=cls.service.run, args=())
+        t.setDaemon(True)
+        t.start()
+        time.sleep(1)
+
+    @classmethod
+    def tearDownClass(cls):
+        shutil.rmtree(cls.code_abs_path)
+
+    def setUp(self):
+        # Print full diff when comparing large dictionaries
+        self.maxDiff = None
+
+    def test_mock_response_is_returned(self):
+        expected = 'Hello from Lambda'
+
+        response = requests.post(self.url + '/2015-03-31/functions/HelloWorld/invocations')
+
+        actual = response.json()
+
+        self.assertEquals(actual, expected)
+        self.assertEquals(response.status_code, 200)
+
+
+class TestLocalEchoLambdaService(TestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.code_abs_path = nodejs_lambda(ECHO_CODE)
+
+        # Let's convert this absolute path to relative path. Let the parent be the CWD, and codeuri be the folder
+        cls.cwd = os.path.dirname(cls.code_abs_path)
+        cls.code_uri = os.path.relpath(cls.code_abs_path, cls.cwd)  # Get relative path with respect to CWD
+
+        cls.function_name = "HelloWorld"
+
+        cls.function = provider.Function(name=cls.function_name, runtime="nodejs4.3", memory=256, timeout=5,
+                                         handler="index.handler", codeuri=cls.code_uri, environment=None,
+                                         rolearn=None)
+
+        cls.mock_function_provider = Mock()
+        cls.mock_function_provider.get.return_value = cls.function
+
+        list_of_function_names = ['HelloWorld']
+
+        cls.service, cls.port, cls.url, cls.scheme = make_service(list_of_function_names, cls.mock_function_provider, cls.cwd)
+        cls.service.create()
+        t = threading.Thread(name='thread', target=cls.service.run, args=())
+        t.setDaemon(True)
+        t.start()
+        time.sleep(1)
+
+    @classmethod
+    def tearDownClass(cls):
+        shutil.rmtree(cls.code_abs_path)
+
+    def setUp(self):
+        # Print full diff when comparing large dictionaries
+        self.maxDiff = None
+
+    def test_mock_response_is_returned(self):
+        expected = {"key1": "value1"}
+
+        response = requests.post(self.url + '/2015-03-31/functions/HelloWorld/invocations', json={"key1": "value1"})
+
+        actual = response.json()
+
+        self.assertEquals(actual, expected)
+        self.assertEquals(response.status_code, 200)
+
+
+def make_service(list_of_function_names, function_provider, cwd):
+    port = random_port()
+    manager = ContainerManager()
+    local_runtime = LambdaRuntime(manager)
+    lambda_runner = LocalLambdaRunner(local_runtime=local_runtime,
+                                      function_provider=function_provider,
+                                      cwd=cwd)
+
+    service = LocalLambdaInvokeService(lambda_runner, port=port, host='127.0.0.1')
+
+    scheme = "http"
+    url = '{}://127.0.0.1:{}'.format(scheme, port)
+    return service, port, url, scheme
+
+def random_port():
+    return random.randint(30000, 40000)
\ No newline at end of file
diff --git a/tests/unit/commands/local/lib/test_local_api_service.py b/tests/unit/commands/local/lib/test_local_api_service.py
index aee6f7f4fa3..d018ad05254 100644
--- a/tests/unit/commands/local/lib/test_local_api_service.py
+++ b/tests/unit/commands/local/lib/test_local_api_service.py
@@ -8,7 +8,7 @@
 from samcli.commands.local.lib.local_api_service import LocalApiService
 from samcli.commands.local.lib.exceptions import NoApisDefined
 from samcli.commands.local.lib.provider import Api
-from samcli.local.apigw.service import Route
+from samcli.local.apigw.local_apigw_service import Route
 
 
 class TestLocalApiService_start(TestCase):
@@ -32,7 +32,7 @@ def setUp(self):
         self.lambda_invoke_context_mock.get_cwd.return_value = self.cwd
         self.lambda_invoke_context_mock.stderr = self.stderr_mock
 
-    @patch("samcli.commands.local.lib.local_api_service.Service")
+    @patch("samcli.commands.local.lib.local_api_service.LocalApigwService")
     @patch("samcli.commands.local.lib.local_api_service.SamApiProvider")
     @patch.object(LocalApiService, "_make_static_dir_path")
     @patch.object(LocalApiService, "_print_routes")
@@ -73,7 +73,7 @@ def test_must_start_service(self,
         self.apigw_service.create.assert_called_with()
         self.apigw_service.run.assert_called_with()
 
-    @patch("samcli.commands.local.lib.local_api_service.Service")
+    @patch("samcli.commands.local.lib.local_api_service.LocalApigwService")
     @patch("samcli.commands.local.lib.local_api_service.SamApiProvider")
     @patch.object(LocalApiService, "_make_static_dir_path")
     @patch.object(LocalApiService, "_print_routes")
diff --git a/tests/unit/local/apigw/test_service.py b/tests/unit/local/apigw/test_local_apigw_service.py
similarity index 74%
rename from tests/unit/local/apigw/test_service.py
rename to tests/unit/local/apigw/test_local_apigw_service.py
index 8fd5b16aa32..3e741e21dc2 100644
--- a/tests/unit/local/apigw/test_service.py
+++ b/tests/unit/local/apigw/test_local_apigw_service.py
@@ -1,11 +1,11 @@
 from unittest import TestCase
-from mock import Mock, patch, MagicMock, ANY
+from mock import Mock, patch, ANY
 import json
 import base64
 
 from parameterized import parameterized, param
 
-from samcli.local.apigw.service import Service, Route, CaseInsensitiveDict
+from samcli.local.apigw.local_apigw_service import LocalApigwService, Route, CaseInsensitiveDict
 from samcli.local.lambdafn.exceptions import FunctionNotFound
 
 
@@ -17,9 +17,14 @@ def setUp(self):
         self.list_of_routes = [self.api_gateway_route]
 
         self.lambda_runner = Mock()
+        self.lambda_runner.is_debugging.return_value = False
 
         self.stderr = Mock()
-        self.service = Service(self.list_of_routes, self.lambda_runner, stderr=self.stderr)
+        self.service = LocalApigwService(self.list_of_routes,
+                                         self.lambda_runner,
+                                         port=3000,
+                                         host='127.0.0.1',
+                                         stderr=self.stderr)
 
     def test_request_must_invoke_lambda(self):
         make_response_mock = Mock()
@@ -44,7 +49,9 @@ def test_request_must_invoke_lambda(self):
                                                      stdout=ANY,
                                                      stderr=self.stderr)
 
-    def test_request_handler_returns_process_stdout_when_making_response(self):
+    @patch('samcli.local.apigw.local_apigw_service.LambdaOutputParser')
+    def test_request_handler_returns_process_stdout_when_making_response(self, lambda_output_parser_mock):
+
         make_response_mock = Mock()
 
         self.service._service_response = make_response_mock
@@ -57,8 +64,7 @@ def test_request_handler_returns_process_stdout_when_making_response(self):
 
         lambda_logs = "logs"
         lambda_response = "response"
-        self.service._get_lambda_output = Mock()
-        self.service._get_lambda_output.return_value = lambda_response, lambda_logs
+        lambda_output_parser_mock.get_lambda_output.return_value = lambda_response, lambda_logs
 
         service_response_mock = Mock()
         service_response_mock.return_value = make_response_mock
@@ -67,7 +73,7 @@ def test_request_handler_returns_process_stdout_when_making_response(self):
         result = self.service._request_handler()
 
         self.assertEquals(result, make_response_mock)
-        self.service._get_lambda_output.assert_called_with(ANY)
+        lambda_output_parser_mock.get_lambda_output.assert_called_with(ANY)
 
         # Make sure the parse method is called only on the returned response and not on the raw data from stdout
         parse_output_mock.assert_called_with(lambda_response, ANY, ANY)
@@ -93,30 +99,6 @@ def test_request_handler_returns_make_response(self):
 
         self.assertEquals(result, make_response_mock)
 
-    def test_runtime_error_raised_when_app_not_created(self):
-        with self.assertRaises(RuntimeError):
-            self.service.run()
-
-    def test_run_starts_service_multithreaded(self):
-        self.service._app = Mock()
-        app_run_mock = Mock()
-        self.service._app.run = app_run_mock
-
-        self.lambda_runner.is_debugging.return_value = False  # multithreaded
-        self.service.run()
-
-        app_run_mock.assert_called_once_with(threaded=True, host='127.0.0.1', port=3000)
-
-    def test_run_starts_service_singlethreaded(self):
-        self.service._app = Mock()
-        app_run_mock = Mock()
-        self.service._app.run = app_run_mock
-
-        self.lambda_runner.is_debugging.return_value = True  # single threaded
-        self.service.run()
-
-        app_run_mock.assert_called_once_with(threaded=False, host='127.0.0.1', port=3000)
-
     def test_create_creates_dict_of_routes(self):
         function_name_1 = Mock()
         function_name_2 = Mock()
@@ -127,7 +109,7 @@ def test_create_creates_dict_of_routes(self):
 
         lambda_runner = Mock()
 
-        service = Service(list_of_routes, lambda_runner)
+        service = LocalApigwService(list_of_routes, lambda_runner)
 
         service.create()
 
@@ -135,7 +117,7 @@ def test_create_creates_dict_of_routes(self):
                                                     '/:POST': api_gateway_route_2
                                                     })
 
-    @patch('samcli.local.apigw.service.Flask')
+    @patch('samcli.local.apigw.local_apigw_service.Flask')
     def test_create_creates_flask_app_with_url_rules(self, flask):
         app_mock = Mock()
         flask.return_value = app_mock
@@ -159,14 +141,14 @@ def test_initalize_creates_default_values(self):
 
     def test_initalize_with_values(self):
         lambda_runner = Mock()
-        local_service = Service([], lambda_runner, static_dir='dir/static', port=5000, host='129.0.0.0')
+        local_service = LocalApigwService([], lambda_runner, static_dir='dir/static', port=5000, host='129.0.0.0')
         self.assertEquals(local_service.port, 5000)
         self.assertEquals(local_service.host, '129.0.0.0')
         self.assertEquals(local_service.routing_list, [])
         self.assertEquals(local_service.static_dir, 'dir/static')
         self.assertEquals(local_service.lambda_runner, lambda_runner)
 
-    @patch('samcli.local.apigw.service.ServiceErrorResponses')
+    @patch('samcli.local.apigw.local_apigw_service.ServiceErrorResponses')
     def test_request_handles_error_when_invoke_cant_find_function(self, service_error_responses_patch):
 
         not_found_response_mock = Mock()
@@ -189,7 +171,7 @@ def test_request_throws_when_invoke_fails(self):
         with self.assertRaises(Exception):
             self.service._request_handler()
 
-    @patch('samcli.local.apigw.service.ServiceErrorResponses')
+    @patch('samcli.local.apigw.local_apigw_service.ServiceErrorResponses')
     def test_request_handler_errors_when_parse_lambda_output_raises_keyerror(self, service_error_responses_patch):
         parse_output_mock = Mock()
         parse_output_mock.side_effect = KeyError()
@@ -206,7 +188,7 @@ def test_request_handler_errors_when_parse_lambda_output_raises_keyerror(self, s
 
         self.assertEquals(result, failure_response_mock)
 
-    @patch('samcli.local.apigw.service.ServiceErrorResponses')
+    @patch('samcli.local.apigw.local_apigw_service.ServiceErrorResponses')
     def test_request_handler_errors_when_get_current_route_fails(self, service_error_responses_patch):
         get_current_route = Mock()
         get_current_route.side_effect = KeyError()
@@ -215,7 +197,7 @@ def test_request_handler_errors_when_get_current_route_fails(self, service_error
         with self.assertRaises(KeyError):
             self.service._request_handler()
 
-    @patch('samcli.local.apigw.service.ServiceErrorResponses')
+    @patch('samcli.local.apigw.local_apigw_service.ServiceErrorResponses')
     def test_request_handler_errors_when_unable_to_read_binary_data(self, service_error_responses_patch):
         _construct_event = Mock()
         _construct_event.side_effect = UnicodeDecodeError("utf8", b"obj", 1, 2, "reason")
@@ -228,7 +210,7 @@ def test_request_handler_errors_when_unable_to_read_binary_data(self, service_er
         result = self.service._request_handler()
         self.assertEquals(result, failure_mock)
 
-    @patch('samcli.local.apigw.service.request')
+    @patch('samcli.local.apigw.local_apigw_service.request')
     def test_get_current_route(self, request_patch):
 
         request_mock = Mock()
@@ -244,7 +226,7 @@ def test_get_current_route(self, request_patch):
 
         self.assertEquals(self.service._get_current_route(request_mock), "function")
 
-    @patch('samcli.local.apigw.service.request')
+    @patch('samcli.local.apigw.local_apigw_service.request')
     def test_get_current_route_keyerror(self, request_patch):
         """
         When the a HTTP request for given method+path combination is allowed by Flask but not in the list of routes,
@@ -265,48 +247,6 @@ def test_get_current_route_keyerror(self, request_patch):
         with self.assertRaises(KeyError):
             self.service._get_current_route(request_mock)
 
-    @parameterized.expand([
-        param(
-            "with both logs and response",
-            b'this\nis\nlog\ndata\n{"a": "b"}', b'this\nis\nlog\ndata', b'{"a": "b"}'
-        ),
-        param(
-            "with response as string",
-            b"logs\nresponse", b"logs", b"response"
-        ),
-        param(
-            "with response only",
-            b'{"a": "b"}', None, b'{"a": "b"}'
-        ),
-        param(
-            "with response only as string",
-            b'this is the response line', None, b'this is the response line'
-        ),
-        param(
-            "with whitespaces",
-            b'log\ndata\n{"a": "b"}  \n\n\n', b"log\ndata", b'{"a": "b"}'
-        ),
-        param(
-            "with empty data",
-            b'', None, b''
-        ),
-        param(
-            "with just new lines",
-            b'\n\n', None, b''
-        ),
-        param(
-            "with no data but with whitespaces",
-            b'\n   \n   \n', b'\n   ', b''   # Log data with whitespaces will be in the output unchanged
-        )
-    ])
-    def test_get_lambda_output_extracts_response(self, test_case_name, stdout_data, expected_logs, expected_response):
-        stdout = Mock()
-        stdout.getvalue.return_value = stdout_data
-
-        response, logs = self.service._get_lambda_output(stdout)
-        self.assertEquals(logs, expected_logs)
-        self.assertEquals(response, expected_response)
-
 
 class TestApiGatewayModel(TestCase):
 
@@ -326,7 +266,7 @@ def test_default_content_type_header_added_with_no_headers(self):
         lambda_output = '{"statusCode": 200, "body": "{\\"message\\":\\"Hello from Lambda\\"}", ' \
                         '"isBase64Encoded": false}'
 
-        (_, headers, _) = Service._parse_lambda_output(lambda_output, binary_types=[], flask_request=Mock())
+        (_, headers, _) = LocalApigwService._parse_lambda_output(lambda_output, binary_types=[], flask_request=Mock())
 
         self.assertIn("Content-Type", headers)
         self.assertEquals(headers["Content-Type"], "application/json")
@@ -335,7 +275,7 @@ def test_default_content_type_header_added_with_empty_headers(self):
         lambda_output = '{"statusCode": 200, "headers":{}, "body": "{\\"message\\":\\"Hello from Lambda\\"}", ' \
                         '"isBase64Encoded": false}'
 
-        (_, headers, _) = Service._parse_lambda_output(lambda_output, binary_types=[], flask_request=Mock())
+        (_, headers, _) = LocalApigwService._parse_lambda_output(lambda_output, binary_types=[], flask_request=Mock())
 
         self.assertIn("Content-Type", headers)
         self.assertEquals(headers["Content-Type"], "application/json")
@@ -344,7 +284,7 @@ def test_custom_content_type_header_is_not_modified(self):
         lambda_output = '{"statusCode": 200, "headers":{"Content-Type": "text/xml"}, "body": "{}", ' \
                         '"isBase64Encoded": false}'
 
-        (_, headers, _) = Service._parse_lambda_output(lambda_output, binary_types=[], flask_request=Mock())
+        (_, headers, _) = LocalApigwService._parse_lambda_output(lambda_output, binary_types=[], flask_request=Mock())
 
         self.assertIn("Content-Type", headers)
         self.assertEquals(headers["Content-Type"], "text/xml")
@@ -353,9 +293,9 @@ def test_extra_values_ignored(self):
         lambda_output = '{"statusCode": 200, "headers": {}, "body": "{\\"message\\":\\"Hello from Lambda\\"}", ' \
                         '"isBase64Encoded": false, "another_key": "some value"}'
 
-        (status_code, headers, body) = Service._parse_lambda_output(lambda_output,
-                                                                    binary_types=[],
-                                                                    flask_request=Mock())
+        (status_code, headers, body) = LocalApigwService._parse_lambda_output(lambda_output,
+                                                                              binary_types=[],
+                                                                              flask_request=Mock())
 
         self.assertEquals(status_code, 200)
         self.assertEquals(headers, {"Content-Type": "application/json"})
@@ -365,15 +305,15 @@ def test_parse_returns_correct_tuple(self):
         lambda_output = '{"statusCode": 200, "headers": {}, "body": "{\\"message\\":\\"Hello from Lambda\\"}", ' \
                         '"isBase64Encoded": false}'
 
-        (status_code, headers, body) = Service._parse_lambda_output(lambda_output,
-                                                                    binary_types=[],
-                                                                    flask_request=Mock())
+        (status_code, headers, body) = LocalApigwService._parse_lambda_output(lambda_output,
+                                                                              binary_types=[],
+                                                                              flask_request=Mock())
 
         self.assertEquals(status_code, 200)
         self.assertEquals(headers, {"Content-Type": "application/json"})
         self.assertEquals(body, '{"message":"Hello from Lambda"}')
 
-    @patch('samcli.local.apigw.service.Service._should_base64_decode_body')
+    @patch('samcli.local.apigw.local_apigw_service.LocalApigwService._should_base64_decode_body')
     def test_parse_returns_decodes_base64_to_binary(self, should_decode_body_patch):
         should_decode_body_patch.return_value = True
 
@@ -384,9 +324,9 @@ def test_parse_returns_decodes_base64_to_binary(self, should_decode_body_patch):
                          "body": base64_body,
                          "isBase64Encoded": False}
 
-        (status_code, headers, body) = Service._parse_lambda_output(json.dumps(lambda_output),
-                                                                    binary_types=['*/*'],
-                                                                    flask_request=Mock())
+        (status_code, headers, body) = LocalApigwService._parse_lambda_output(json.dumps(lambda_output),
+                                                                              binary_types=['*/*'],
+                                                                              flask_request=Mock())
 
         self.assertEquals(status_code, 200)
         self.assertEquals(headers, {"Content-Type": "application/octet-stream"})
@@ -397,40 +337,40 @@ def test_status_code_not_int(self):
                         '"isBase64Encoded": false}'
 
         with self.assertRaises(TypeError):
-            Service._parse_lambda_output(lambda_output,
-                                         binary_types=[],
-                                         flask_request=Mock())
+            LocalApigwService._parse_lambda_output(lambda_output,
+                                                   binary_types=[],
+                                                   flask_request=Mock())
 
     def test_status_code_negative_int(self):
         lambda_output = '{"statusCode": -1, "headers": {}, "body": "{\\"message\\":\\"Hello from Lambda\\"}", ' \
                             '"isBase64Encoded": false}'
 
         with self.assertRaises(TypeError):
-            Service._parse_lambda_output(lambda_output,
-                                         binary_types=[],
-                                         flask_request=Mock())
+            LocalApigwService._parse_lambda_output(lambda_output,
+                                                   binary_types=[],
+                                                   flask_request=Mock())
 
     def test_lambda_output_list_not_dict(self):
         lambda_output = '[]'
 
         with self.assertRaises(TypeError):
-            Service._parse_lambda_output(lambda_output,
-                                         binary_types=[],
-                                         flask_request=Mock())
+            LocalApigwService._parse_lambda_output(lambda_output,
+                                                   binary_types=[],
+                                                   flask_request=Mock())
 
     def test_lambda_output_not_json_serializable(self):
         lambda_output = 'some str'
 
         with self.assertRaises(ValueError):
-            Service._parse_lambda_output(lambda_output, binary_types=[], flask_request=Mock())
+            LocalApigwService._parse_lambda_output(lambda_output, binary_types=[], flask_request=Mock())
 
     def test_properties_are_null(self):
         lambda_output = '{"statusCode": 0, "headers": null, "body": null, ' \
                         '"isBase64Encoded": null}'
 
-        (status_code, headers, body) = Service._parse_lambda_output(lambda_output,
-                                                                    binary_types=[],
-                                                                    flask_request=Mock())
+        (status_code, headers, body) = LocalApigwService._parse_lambda_output(lambda_output,
+                                                                              binary_types=[],
+                                                                              flask_request=Mock())
 
         self.assertEquals(status_code, 200)
         self.assertEquals(headers, {"Content-Type": "application/json"})
@@ -467,17 +407,17 @@ def setUp(self):
         self.expected_dict = json.loads(expected)
 
     def test_construct_event_with_data(self):
-        actual_event_str = Service._construct_event(self.request_mock, 3000, binary_types=[])
+        actual_event_str = LocalApigwService._construct_event(self.request_mock, 3000, binary_types=[])
         self.assertEquals(json.loads(actual_event_str), self.expected_dict)
 
     def test_construct_event_no_data(self):
         self.request_mock.get_data.return_value = None
         self.expected_dict["body"] = None
 
-        actual_event_str = Service._construct_event(self.request_mock, 3000, binary_types=[])
+        actual_event_str = LocalApigwService._construct_event(self.request_mock, 3000, binary_types=[])
         self.assertEquals(json.loads(actual_event_str), self.expected_dict)
 
-    @patch('samcli.local.apigw.service.Service._should_base64_encode')
+    @patch('samcli.local.apigw.local_apigw_service.LocalApigwService._should_base64_encode')
     def test_construct_event_with_binary_data(self, should_base64_encode_patch):
         should_base64_encode_patch.return_value = True
 
@@ -488,51 +428,31 @@ def test_construct_event_with_binary_data(self, should_base64_encode_patch):
         self.expected_dict["body"] = base64_body
         self.expected_dict["isBase64Encoded"] = True
 
-        actual_event_str = Service._construct_event(self.request_mock, 3000, binary_types=[])
+        actual_event_str = LocalApigwService._construct_event(self.request_mock, 3000, binary_types=[])
         self.assertEquals(json.loads(actual_event_str), self.expected_dict)
 
     def test_query_string_params_with_empty_params(self):
         request_mock = Mock()
         request_mock.args = {}
 
-        actual_query_string = Service._query_string_params(request_mock)
+        actual_query_string = LocalApigwService._query_string_params(request_mock)
         self.assertEquals(actual_query_string, {})
 
     def test_query_string_params_with_param_value_being_empty_list(self):
         request_mock = Mock()
         request_mock.args = {"param": []}
 
-        actual_query_string = Service._query_string_params(request_mock)
+        actual_query_string = LocalApigwService._query_string_params(request_mock)
         self.assertEquals(actual_query_string, {"param": ""})
 
     def test_query_string_params_with_param_value_being_non_empty_list(self):
         request_mock = Mock()
         request_mock.args = {"param": ["a", "b"]}
 
-        actual_query_string = Service._query_string_params(request_mock)
+        actual_query_string = LocalApigwService._query_string_params(request_mock)
         self.assertEquals(actual_query_string, {"param": "b"})
 
 
-class TestService_service_response(TestCase):
-
-    @patch('samcli.local.apigw.service.Response')
-    def test_service_response(self, flask_response_patch):
-        flask_response_mock = MagicMock()
-
-        flask_response_patch.return_value = flask_response_mock
-
-        body = "this is the body"
-        status_code = 200
-        headers = {"Content-Type": "application/json"}
-
-        actual_response = Service._service_response(body, headers, status_code)
-
-        flask_response_patch.assert_called_once_with("this is the body")
-
-        self.assertEquals(actual_response.status_code, 200)
-        self.assertEquals(actual_response.headers, {"Content-Type": "application/json"})
-
-
 class TestService_should_base64_encode(TestCase):
 
     @parameterized.expand([
@@ -541,13 +461,13 @@ class TestService_should_base64_encode(TestCase):
         param("*/* is in binary types with no mimetype defined", ['*/*'], None)
     ])
     def test_should_base64_encode_returns_true(self, test_case_name, binary_types, mimetype):
-        self.assertTrue(Service._should_base64_encode(binary_types, mimetype))
+        self.assertTrue(LocalApigwService._should_base64_encode(binary_types, mimetype))
 
     @parameterized.expand([
         param("Mimetype is not in binary types", ['image/gif'], "application/octet-stream")
     ])
     def test_should_base64_encode_returns_false(self, test_case_name, binary_types, mimetype):
-        self.assertFalse(Service._should_base64_encode(binary_types, mimetype))
+        self.assertFalse(LocalApigwService._should_base64_encode(binary_types, mimetype))
 
 
 class TestService_CaseInsensiveDict(TestCase):
diff --git a/tests/unit/local/lambda_service/__init__.py b/tests/unit/local/lambda_service/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/unit/local/lambda_service/test_local_lambda_invoke_service.py b/tests/unit/local/lambda_service/test_local_lambda_invoke_service.py
new file mode 100644
index 00000000000..dca78c5c25b
--- /dev/null
+++ b/tests/unit/local/lambda_service/test_local_lambda_invoke_service.py
@@ -0,0 +1,102 @@
+from unittest import TestCase
+from mock import Mock, patch, ANY
+
+from samcli.local.lambda_service.local_lambda_invoke_service import LocalLambdaInvokeService
+from samcli.local.lambdafn.exceptions import FunctionNotFound
+
+
+class TestLocalLambdaService(TestCase):
+
+    def test_initalize_creates_default_values(self):
+        lambda_runner_mock = Mock()
+        service = LocalLambdaInvokeService(lambda_runner=lambda_runner_mock, port=3001, host='127.0.0.1')
+        self.assertEquals(service.port, 3001)
+        self.assertEquals(service.host, '127.0.0.1')
+        self.assertEquals(service.lambda_runner, lambda_runner_mock)
+        self.assertIsNone(service.stderr)
+
+    def test_initalize_with_values(self):
+        lambda_runner_mock = Mock()
+        stderr_mock = Mock()
+        local_service = LocalLambdaInvokeService(lambda_runner_mock, port=5000, host='129.0.0.0', stderr=stderr_mock)
+        self.assertEquals(local_service.port, 5000)
+        self.assertEquals(local_service.host, '129.0.0.0')
+        self.assertEquals(local_service.stderr, stderr_mock)
+        self.assertEquals(local_service.lambda_runner, lambda_runner_mock)
+
+    @patch('samcli.local.lambda_service.local_lambda_invoke_service.LocalLambdaInvokeService._construct_error_handling')
+    @patch('samcli.local.lambda_service.local_lambda_invoke_service.Flask')
+    def test_create_service_endpoints(self, flask_mock, error_handling_mock):
+        app_mock = Mock()
+        flask_mock.return_value = app_mock
+
+        error_handling_mock.return_value = Mock()
+
+        lambda_runner_mock = Mock()
+        service = LocalLambdaInvokeService(lambda_runner=lambda_runner_mock, port=3000, host='localhost')
+
+        service.create()
+
+        app_mock.add_url_rule.assert_called_once_with('/2015-03-31/functions/<function_name>/invocations',
+                                                      endpoint='/2015-03-31/functions/<function_name>/invocations',
+                                                      view_func=service._invoke_request_handler,
+                                                      methods=['POST'],
+                                                      provide_automatic_options=False)
+
+    @patch('samcli.local.lambda_service.local_lambda_invoke_service.LocalLambdaInvokeService._service_response')
+    @patch('samcli.local.lambda_service.local_lambda_invoke_service.LambdaOutputParser')
+    @patch('samcli.local.lambda_service.local_lambda_invoke_service.request')
+    def test_invoke_request_handler(self, request_mock, lambda_output_parser_mock, service_response_mock):
+        lambda_output_parser_mock.get_lambda_output.return_value = 'hello world', None
+        service_response_mock.return_value = 'request response'
+        request_mock.get_data.return_value = b'{}'
+
+        lambda_runner_mock = Mock()
+        service = LocalLambdaInvokeService(lambda_runner=lambda_runner_mock, port=3000, host='localhost')
+
+        response = service._invoke_request_handler(function_name='HelloWorld')
+
+        self.assertEquals(response, 'request response')
+
+        lambda_runner_mock.invoke.assert_called_once_with('HelloWorld', '{}', stdout=ANY, stderr=None)
+        service_response_mock.assert_called_once_with('hello world', {'Content-Type': 'application/json'}, 200)
+
+    @patch('samcli.local.lambda_service.local_lambda_invoke_service.request')
+    def test_invoke_request_handler_on_incorrect_path(self, request_mock):
+        request_mock.get_data.return_value = b'{}'
+        lambda_runner_mock = Mock()
+        lambda_runner_mock.invoke.side_effect = FunctionNotFound
+        service = LocalLambdaInvokeService(lambda_runner=lambda_runner_mock, port=3000, host='localhost')
+
+        with self.assertRaises(Exception):
+            service._invoke_request_handler(function_name='NotFound')
+
+        lambda_runner_mock.invoke.assert_called_once_with('NotFound', '{}', stdout=ANY, stderr=None)
+
+    @patch('samcli.local.lambda_service.local_lambda_invoke_service.LocalLambdaInvokeService._service_response')
+    @patch('samcli.local.lambda_service.local_lambda_invoke_service.LambdaOutputParser')
+    @patch('samcli.local.lambda_service.local_lambda_invoke_service.request')
+    def test_request_handler_returns_process_stdout_when_making_response(self, request_mock, lambda_output_parser_mock,
+                                                                         service_response_mock):
+        request_mock.get_data.return_value = b'{}'
+
+        lambda_logs = "logs"
+        lambda_response = "response"
+        lambda_output_parser_mock.get_lambda_output.return_value = lambda_response, lambda_logs
+
+        service_response_mock.return_value = 'request response'
+
+        lambda_runner_mock = Mock()
+        stderr_mock = Mock()
+        service = LocalLambdaInvokeService(lambda_runner=lambda_runner_mock,
+                                           port=3000,
+                                           host='localhost',
+                                           stderr=stderr_mock)
+
+        result = service._invoke_request_handler(function_name='HelloWorld')
+
+        self.assertEquals(result, 'request response')
+        lambda_output_parser_mock.get_lambda_output.assert_called_with(ANY)
+
+        # Make sure the logs are written to stderr
+        stderr_mock.write.assert_called_with(lambda_logs)
diff --git a/tests/unit/local/services/__init__.py b/tests/unit/local/services/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/unit/local/services/test_base_local_service.py b/tests/unit/local/services/test_base_local_service.py
new file mode 100644
index 00000000000..428b3f3f274
--- /dev/null
+++ b/tests/unit/local/services/test_base_local_service.py
@@ -0,0 +1,109 @@
+from unittest import TestCase
+from mock import Mock, patch
+
+from parameterized import parameterized, param
+
+from samcli.local.services.base_local_service import BaseLocalService, LambdaOutputParser
+
+
+class TestLocalHostRunner(TestCase):
+
+    def test_runtime_error_raised_when_app_not_created(self):
+        is_debugging = False
+        service = BaseLocalService(is_debugging=is_debugging, port=3000, host='127.0.0.1')
+
+        with self.assertRaises(RuntimeError):
+            service.run()
+
+    def test_run_starts_service_multithreaded(self):
+        is_debugging = False  # multithreaded
+        service = BaseLocalService(is_debugging=is_debugging, port=3000, host='127.0.0.1')
+
+        service._app = Mock()
+        app_run_mock = Mock()
+        service._app.run = app_run_mock
+
+        service.run()
+
+        app_run_mock.assert_called_once_with(threaded=True, host='127.0.0.1', port=3000)
+
+    def test_run_starts_service_singlethreaded(self):
+        is_debugging = True  # singlethreaded
+        service = BaseLocalService(is_debugging=is_debugging, port=3000, host='127.0.0.1')
+
+        service._app = Mock()
+        app_run_mock = Mock()
+        service._app.run = app_run_mock
+
+        service.run()
+
+        app_run_mock.assert_called_once_with(threaded=False, host='127.0.0.1', port=3000)
+
+    @patch('samcli.local.services.base_local_service.Response')
+    def test_service_response(self, flask_response_patch):
+        flask_response_mock = Mock()
+
+        flask_response_patch.return_value = flask_response_mock
+
+        body = "this is the body"
+        status_code = 200
+        headers = {"Content-Type": "application/json"}
+
+        actual_response = BaseLocalService._service_response(body, headers, status_code)
+
+        flask_response_patch.assert_called_once_with("this is the body")
+
+        self.assertEquals(actual_response.status_code, 200)
+        self.assertEquals(actual_response.headers, {"Content-Type": "application/json"})
+
+    def test_create_returns_not_implemented(self):
+        is_debugging = False
+        service = BaseLocalService(is_debugging=is_debugging, port=3000, host='127.0.0.1')
+
+        with self.assertRaises(NotImplementedError):
+            service.create()
+
+
+class TestLambdaOutputParser(TestCase):
+
+    @parameterized.expand([
+        param(
+            "with both logs and response",
+            b'this\nis\nlog\ndata\n{"a": "b"}', b'this\nis\nlog\ndata', b'{"a": "b"}'
+        ),
+        param(
+            "with response as string",
+            b"logs\nresponse", b"logs", b"response"
+        ),
+        param(
+            "with response only",
+            b'{"a": "b"}', None, b'{"a": "b"}'
+        ),
+        param(
+            "with response only as string",
+            b'this is the response line', None, b'this is the response line'
+        ),
+        param(
+            "with whitespaces",
+            b'log\ndata\n{"a": "b"}  \n\n\n', b"log\ndata", b'{"a": "b"}'
+        ),
+        param(
+            "with empty data",
+            b'', None, b''
+        ),
+        param(
+            "with just new lines",
+            b'\n\n', None, b''
+        ),
+        param(
+            "with no data but with whitespaces",
+            b'\n   \n   \n', b'\n   ', b''  # Log data with whitespaces will be in the output unchanged
+        )
+    ])
+    def test_get_lambda_output_extracts_response(self, test_case_name, stdout_data, expected_logs, expected_response):
+        stdout = Mock()
+        stdout.getvalue.return_value = stdout_data
+
+        response, logs = LambdaOutputParser.get_lambda_output(stdout)
+        self.assertEquals(logs, expected_logs)
+        self.assertEquals(response, expected_response)
