diff --git a/README.md b/README.md
index 8a580c40..b4d59c53 100644
--- a/README.md
+++ b/README.md
@@ -129,13 +129,18 @@ You can configure the Functions Framework using command-line flags or environmen
 | `--host`           | `HOST`                    | The host on which the Functions Framework listens for requests. Default: `0.0.0.0`                                                                                                               |
 | `--port`           | `PORT`                    | The port on which the Functions Framework listens for requests. Default: `8080`                                                                                                                  |
 | `--target`         | `FUNCTION_TARGET`         | The name of the exported function to be invoked in response to requests. Default: `function`                                                                                                     |
-| `--signature-type` | `FUNCTION_SIGNATURE_TYPE` | The signature used when writing your function. Controls unmarshalling rules and determines which arguments are used to invoke your function. Default: `http`; accepted values: `http` or `event` |
+| `--signature-type` | `FUNCTION_SIGNATURE_TYPE` | The signature used when writing your function. Controls unmarshalling rules and determines which arguments are used to invoke your function. Default: `http`; accepted values: `http` or `event` or `cloudevent` |
 | `--source`         | `FUNCTION_SOURCE`         | The path to the file containing your function. Default: `main.py` (in the current working directory)                                                                                             |
 | `--debug`          | `DEBUG`                   | A flag that allows to run functions-framework to run in debug mode, including live reloading. Default: `False`                                                                                   |
 
-# Enable CloudEvents
 
-The Functions Framework can unmarshall incoming [CloudEvents](http://cloudevents.io) payloads to `data` and `context` objects.  These will be passed as arguments to your function when it receives a request.  Note that your function must use the event-style function signature:
+# Enable Google Cloud Functions Events
+
+The Functions Framework can unmarshall incoming
+Google Cloud Functions [event](https://cloud.google.com/functions/docs/concepts/events-triggers#events) payloads to `data` and `context` objects.
+These will be passed as arguments to your function when it receives a request.
+Note that your function must use the `event`-style function signature:
+
 
 ```python
 def hello(data, context):
@@ -143,13 +148,38 @@ def hello(data, context):
     print(context)
 ```
 
-To enable automatic unmarshalling, set the function signature type to `event` using the `--signature-type` command-line flag or the `FUNCTION_SIGNATURE_TYPE` environment variable. By default, the HTTP signature type will be used and automatic event unmarshalling will be disabled.
+To enable automatic unmarshalling, set the function signature type to `event`
+using a command-line flag or an environment variable. By default, the HTTP
+signature will be used and automatic event unmarshalling will be disabled.
+
+For more details on this signature type, check out the Google Cloud Functions
+documentation on
+[background functions](https://cloud.google.com/functions/docs/writing/background#cloud_pubsub_example).
+
+See the [running example](examples/cloud_run_event).
+
+# Enable CloudEvents
+
+The Functions Framework can unmarshall incoming
+[CloudEvent](http://cloudevents.io) payloads to a `cloudevent` object.
+It will be passed as an argument to your function when it receives a request.
+Note that your function must use the `cloudevent`-style function signature
+
+
+```python
+def hello(cloudevent):
+    print("Received event with ID: %s" % cloudevent.EventID())
+    return 200
+```
+
+To enable automatic unmarshalling, set the function signature type to `cloudevent` using the `--signature-type` command-line flag or the `FUNCTION_SIGNATURE_TYPE` environment variable. By default, the HTTP signature type will be used and automatic event unmarshalling will be disabled.
 
-For more details on this signature type, check out the Google Cloud Functions documentation on [background functions](https://cloud.google.com/functions/docs/writing/background#cloud_pubsub_example).
+See the [running example](examples/cloud_run_cloudevents).
 
 # Advanced Examples
 
-More advanced guides can be found in the [`examples/`](./examples/) directory.
+More advanced guides can be found in the [`examples/`](./examples/) directory. You can also find examples
+on using the CloudEvent Python SDK [here](https://github.com/cloudevents/sdk-python).
 
 # Contributing
 
diff --git a/examples/README.md b/examples/README.md
index 33ce2a76..fc027bfe 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -1,4 +1,5 @@
 # Python Functions Frameworks Examples
 
 * [`cloud_run_http`](./cloud_run_http/) - Deploying an HTTP function to [Cloud Run](http://cloud.google.com/run) with the Functions Framework
-* [`cloud_run_event`](./cloud_run_event/) - Deploying a CloudEvent function to [Cloud Run](http://cloud.google.com/run) with the Functions Framework
+* [`cloud_run_event`](./cloud_run_event/) - Deploying a [Google Cloud Functions Event](https://cloud.google.com/functions/docs/concepts/events-triggers#events) function to [Cloud Run](http://cloud.google.com/run) with the Functions Framework
+* [`cloud_run_cloudevents`](./cloud_run_cloudevents/) - Deploying a [CloudEvent](https://github.com/cloudevents/sdk-python) function to [Cloud Run](http://cloud.google.com/run) with the Functions Framework
diff --git a/examples/cloud_run_cloudevents/Dockerfile b/examples/cloud_run_cloudevents/Dockerfile
new file mode 100644
index 00000000..10163c5f
--- /dev/null
+++ b/examples/cloud_run_cloudevents/Dockerfile
@@ -0,0 +1,15 @@
+# Use the official Python image.
+# https://hub.docker.com/_/python
+FROM python:3.7-slim
+
+# Copy local code to the container image.
+ENV APP_HOME /app
+WORKDIR $APP_HOME
+COPY . .
+
+# Install production dependencies.
+RUN pip install gunicorn cloudevents functions-framework
+RUN pip install -r requirements.txt
+
+# Run the web service on container startup.
+CMD exec functions-framework --target=hello --signature-type=cloudevent
diff --git a/examples/cloud_run_cloudevents/README.md b/examples/cloud_run_cloudevents/README.md
new file mode 100644
index 00000000..0f3c8fe0
--- /dev/null
+++ b/examples/cloud_run_cloudevents/README.md
@@ -0,0 +1,52 @@
+# Deploying a CloudEvent function to Cloud Run with the Functions Framework
+This sample uses the [Cloud Events SDK](https://github.com/cloudevents/sdk-python) to send and receive a CloudEvent on Cloud Run.
+
+## How to run this locally
+Build the Docker image:
+
+```commandline
+docker build --tag ff_example .
+```
+
+Run the image and bind the correct ports:
+
+```commandline
+docker run -p:8080:8080 ff_example
+```
+
+Send an event to the container:
+
+```python
+from cloudevents.sdk import converters
+from cloudevents.sdk import marshaller
+from cloudevents.sdk.converters import structured
+from cloudevents.sdk.event import v1
+import requests
+import json
+
+def run_structured(event, url):
+    http_marshaller = marshaller.NewDefaultHTTPMarshaller()
+    structured_headers, structured_data = http_marshaller.ToRequest(
+        event, converters.TypeStructured, json.dumps
+    )
+    print("structured CloudEvent")
+    print(structured_data.getvalue())
+
+    response = requests.post(url,
+                             headers=structured_headers,
+                             data=structured_data.getvalue())
+    response.raise_for_status()
+
+event = (
+    v1.Event()
+    .SetContentType("application/json")
+    .SetData('{"name":"john"}')
+    .SetEventID("my-id")
+    .SetSource("from-galaxy-far-far-away")
+    .SetEventTime("tomorrow")
+    .SetEventType("cloudevent.greet.you")
+)
+
+run_structured(event, "http://0.0.0.0:8080/")
+
+```
diff --git a/examples/cloud_run_cloudevents/main.py b/examples/cloud_run_cloudevents/main.py
new file mode 100644
index 00000000..94b2734a
--- /dev/null
+++ b/examples/cloud_run_cloudevents/main.py
@@ -0,0 +1,21 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This sample creates a function that accepts a Cloud Event per
+# https://github.com/cloudevents/sdk-python
+import sys
+
+
+def hello(cloudevent):
+    print("Received event with ID: %s" % cloudevent.EventID(), file=sys.stdout, flush=True)
diff --git a/examples/cloud_run_cloudevents/requirements.txt b/examples/cloud_run_cloudevents/requirements.txt
new file mode 100644
index 00000000..33c5f99f
--- /dev/null
+++ b/examples/cloud_run_cloudevents/requirements.txt
@@ -0,0 +1 @@
+# Optionally include additional dependencies here
diff --git a/examples/cloud_run_event/Dockerfile b/examples/cloud_run_event/Dockerfile
index b3e7ffeb..6b31c042 100644
--- a/examples/cloud_run_event/Dockerfile
+++ b/examples/cloud_run_event/Dockerfile
@@ -12,4 +12,4 @@ RUN pip install gunicorn functions-framework
 RUN pip install -r requirements.txt
 
 # Run the web service on container startup.
-CMD exec functions-framework --target=hello --signature-type=event
+CMD exec functions-framework --target=hello --signature_type=event
diff --git a/examples/cloud_run_event/README.md b/examples/cloud_run_event/README.md
new file mode 100644
index 00000000..62d34cca
--- /dev/null
+++ b/examples/cloud_run_event/README.md
@@ -0,0 +1,3 @@
+# Google Cloud Functions Events Example
+This example demonstrates how to write an event function. Note that you can also use [CloudEvents](https://github.com/cloudevents/sdk-python)
+([example](../cloud_run_cloudevents)), which is a different construct. 
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 2a16fe7f..2509ca22 100644
--- a/setup.py
+++ b/setup.py
@@ -52,6 +52,7 @@
         "click>=7.0,<8.0",
         "watchdog>=0.10.0",
         "gunicorn>=19.2.0,<21.0; platform_system!='Windows'",
+        "cloudevents<1.0",
     ],
     extras_require={"test": ["pytest", "tox"]},
     entry_points={
diff --git a/src/functions_framework/__init__.py b/src/functions_framework/__init__.py
index ce0e2fbf..5f5422a7 100644
--- a/src/functions_framework/__init__.py
+++ b/src/functions_framework/__init__.py
@@ -12,13 +12,19 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import functools
+import enum
 import importlib.util
+import io
+import json
 import os.path
 import pathlib
 import sys
 import types
 
+import cloudevents.sdk
+import cloudevents.sdk.event
+import cloudevents.sdk.event.v1
+import cloudevents.sdk.marshaller
 import flask
 import werkzeug
 
@@ -35,6 +41,12 @@
 DEFAULT_SIGNATURE_TYPE = "http"
 
 
+class _EventType(enum.Enum):
+    LEGACY = 1
+    CLOUDEVENT_BINARY = 2
+    CLOUDEVENT_STRUCTURED = 3
+
+
 class _Event(object):
     """Event passed to background functions."""
 
@@ -67,38 +79,83 @@ def view_func(path):
     return view_func
 
 
-def _is_binary_cloud_event(request):
-    return (
+def _get_cloudevent_version():
+    return cloudevents.sdk.event.v1.Event()
+
+
+def _run_legacy_event(function, request):
+    event_data = request.get_json()
+    if not event_data:
+        flask.abort(400)
+    event_object = _Event(**event_data)
+    data = event_object.data
+    context = Context(**event_object.context)
+    function(data, context)
+
+
+def _run_binary_cloudevent(function, request, cloudevent_def):
+    data = io.BytesIO(request.get_data())
+    http_marshaller = cloudevents.sdk.marshaller.NewDefaultHTTPMarshaller()
+    event = http_marshaller.FromRequest(
+        cloudevent_def, request.headers, data, json.load
+    )
+
+    function(event)
+
+
+def _run_structured_cloudevent(function, request, cloudevent_def):
+    data = io.StringIO(request.get_data(as_text=True))
+    m = cloudevents.sdk.marshaller.NewDefaultHTTPMarshaller()
+    event = m.FromRequest(cloudevent_def, request.headers, data, json.loads)
+    function(event)
+
+
+def _get_event_type(request):
+    if (
         request.headers.get("ce-type")
         and request.headers.get("ce-specversion")
         and request.headers.get("ce-source")
         and request.headers.get("ce-id")
-    )
+    ):
+        return _EventType.CLOUDEVENT_BINARY
+    elif request.headers.get("Content-Type") == "application/cloudevents+json":
+        return _EventType.CLOUDEVENT_STRUCTURED
+    else:
+        return _EventType.LEGACY
 
 
 def _event_view_func_wrapper(function, request):
     def view_func(path):
-        if _is_binary_cloud_event(request):
-            # Support CloudEvents in binary content mode, with data being the
-            # whole request body and context attributes retrieved from request
-            # headers.
-            data = request.get_data()
-            context = Context(
-                eventId=request.headers.get("ce-eventId"),
-                timestamp=request.headers.get("ce-timestamp"),
-                eventType=request.headers.get("ce-eventType"),
-                resource=request.headers.get("ce-resource"),
+        if _get_event_type(request) == _EventType.LEGACY:
+            _run_legacy_event(function, request)
+        else:
+            # here for defensive backwards compatibility in case we make a mistake in rollout.
+            flask.abort(
+                400,
+                description="The FUNCTION_SIGNATURE_TYPE for this function is set to event "
+                "but no Google Cloud Functions Event was given. If you are using CloudEvents set "
+                "FUNCTION_SIGNATURE_TYPE=cloudevent",
             )
-            function(data, context)
+
+        return "OK"
+
+    return view_func
+
+
+def _cloudevent_view_func_wrapper(function, request):
+    def view_func(path):
+        cloudevent_def = _get_cloudevent_version()
+        event_type = _get_event_type(request)
+        if event_type == _EventType.CLOUDEVENT_STRUCTURED:
+            _run_structured_cloudevent(function, request, cloudevent_def)
+        elif event_type == _EventType.CLOUDEVENT_BINARY:
+            _run_binary_cloudevent(function, request, cloudevent_def)
         else:
-            # This is a regular CloudEvent
-            event_data = request.get_json()
-            if not event_data:
-                flask.abort(400)
-            event_object = _Event(**event_data)
-            data = event_object.data
-            context = Context(**event_object.context)
-            function(data, context)
+            flask.abort(
+                400,
+                description="Function was defined with FUNCTION_SIGNATURE_TYPE=cloudevent "
+                " but it did not receive a cloudevent as a request.",
+            )
 
         return "OK"
 
@@ -179,19 +236,27 @@ def create_app(target=None, source=None, signature_type=None):
         app.url_map.add(werkzeug.routing.Rule("/<path:path>", endpoint="run"))
         app.view_functions["run"] = _http_view_func_wrapper(function, flask.request)
         app.view_functions["error"] = lambda: flask.abort(404, description="Not Found")
-    elif signature_type == "event":
+    elif signature_type == "event" or signature_type == "cloudevent":
         app.url_map.add(
             werkzeug.routing.Rule(
-                "/", defaults={"path": ""}, endpoint="run", methods=["POST"]
+                "/", defaults={"path": ""}, endpoint=signature_type, methods=["POST"]
             )
         )
         app.url_map.add(
-            werkzeug.routing.Rule("/<path:path>", endpoint="run", methods=["POST"])
+            werkzeug.routing.Rule(
+                "/<path:path>", endpoint=signature_type, methods=["POST"]
+            )
         )
-        app.view_functions["run"] = _event_view_func_wrapper(function, flask.request)
+
         # Add a dummy endpoint for GET /
         app.url_map.add(werkzeug.routing.Rule("/", endpoint="get", methods=["GET"]))
         app.view_functions["get"] = lambda: ""
+
+        # Add the view functions
+        app.view_functions["event"] = _event_view_func_wrapper(function, flask.request)
+        app.view_functions["cloudevent"] = _cloudevent_view_func_wrapper(
+            function, flask.request
+        )
     else:
         raise FunctionsFrameworkException(
             "Invalid signature type: {signature_type}".format(
diff --git a/src/functions_framework/_cli.py b/src/functions_framework/_cli.py
index 4fe6e427..663ea50f 100644
--- a/src/functions_framework/_cli.py
+++ b/src/functions_framework/_cli.py
@@ -26,7 +26,7 @@
 @click.option(
     "--signature-type",
     envvar="FUNCTION_SIGNATURE_TYPE",
-    type=click.Choice(["http", "event"]),
+    type=click.Choice(["http", "event", "cloudevent"]),
     default="http",
 )
 @click.option("--host", envvar="HOST", type=click.STRING, default="0.0.0.0")
diff --git a/tests/test_cloudevent_functions.py b/tests/test_cloudevent_functions.py
new file mode 100644
index 00000000..17e6f23c
--- /dev/null
+++ b/tests/test_cloudevent_functions.py
@@ -0,0 +1,120 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import json
+import pathlib
+
+import cloudevents.sdk
+import cloudevents.sdk.event.v1
+import cloudevents.sdk.event.v03
+import cloudevents.sdk.marshaller
+import pytest
+
+from functions_framework import LazyWSGIApp, create_app, exceptions
+
+TEST_FUNCTIONS_DIR = pathlib.Path(__file__).resolve().parent / "test_functions"
+
+# Python 3.5: ModuleNotFoundError does not exist
+try:
+    _ModuleNotFoundError = ModuleNotFoundError
+except:
+    _ModuleNotFoundError = ImportError
+
+
+@pytest.fixture
+def cloudevent_1_0():
+    event = (
+        cloudevents.sdk.event.v1.Event()
+        .SetContentType("application/json")
+        .SetData('{"name":"john"}')
+        .SetEventID("my-id")
+        .SetSource("from-galaxy-far-far-away")
+        .SetEventTime("tomorrow")
+        .SetEventType("cloudevent.greet.you")
+    )
+    return event
+
+
+@pytest.fixture
+def cloudevent_0_3():
+    event = (
+        cloudevents.sdk.event.v03.Event()
+        .SetContentType("application/json")
+        .SetData('{"name":"john"}')
+        .SetEventID("my-id")
+        .SetSource("from-galaxy-far-far-away")
+        .SetEventTime("tomorrow")
+        .SetEventType("cloudevent.greet.you")
+    )
+    return event
+
+
+def test_event_1_0(cloudevent_1_0):
+    source = TEST_FUNCTIONS_DIR / "cloudevents" / "main.py"
+    target = "function"
+
+    client = create_app(target, source, "cloudevent").test_client()
+
+    m = cloudevents.sdk.marshaller.NewDefaultHTTPMarshaller()
+    structured_headers, structured_data = m.ToRequest(
+        cloudevent_1_0, cloudevents.sdk.converters.TypeStructured, json.dumps
+    )
+
+    resp = client.post("/", headers=structured_headers, data=structured_data.getvalue())
+    assert resp.status_code == 200
+    assert resp.data == b"OK"
+
+
+def test_binary_event_1_0(cloudevent_1_0):
+    source = TEST_FUNCTIONS_DIR / "cloudevents" / "main.py"
+    target = "function"
+
+    client = create_app(target, source, "cloudevent").test_client()
+
+    m = cloudevents.sdk.marshaller.NewDefaultHTTPMarshaller()
+
+    binary_headers, binary_data = m.ToRequest(
+        cloudevent_1_0, cloudevents.sdk.converters.TypeBinary, json.dumps
+    )
+
+    resp = client.post("/", headers=binary_headers, data=binary_data)
+
+    assert resp.status_code == 200
+    assert resp.data == b"OK"
+
+
+def test_event_0_3(cloudevent_0_3):
+    source = TEST_FUNCTIONS_DIR / "cloudevents" / "main.py"
+    target = "function"
+
+    client = create_app(target, source, "cloudevent").test_client()
+
+    m = cloudevents.sdk.marshaller.NewDefaultHTTPMarshaller()
+    structured_headers, structured_data = m.ToRequest(
+        cloudevent_0_3, cloudevents.sdk.converters.TypeStructured, json.dumps
+    )
+
+    resp = client.post("/", headers=structured_headers, data=structured_data.getvalue())
+    assert resp.status_code == 200
+    assert resp.data == b"OK"
+
+
+def test_non_cloudevent_():
+    source = TEST_FUNCTIONS_DIR / "cloudevents" / "main.py"
+    target = "function"
+
+    client = create_app(target, source, "cloudevent").test_client()
+
+    resp = client.post("/", json="{not_event}")
+    assert resp.status_code == 400
+    assert resp.data != b"OK"
diff --git a/tests/test_event_functions.py b/tests/test_event_functions.py
new file mode 100644
index 00000000..7b274672
--- /dev/null
+++ b/tests/test_event_functions.py
@@ -0,0 +1,213 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import json
+import pathlib
+import re
+
+import cloudevents.sdk
+import cloudevents.sdk.event.v1
+import cloudevents.sdk.marshaller
+import pytest
+
+from functions_framework import LazyWSGIApp, create_app, exceptions
+
+TEST_FUNCTIONS_DIR = pathlib.Path(__file__).resolve().parent / "test_functions"
+
+# Python 3.5: ModuleNotFoundError does not exist
+try:
+    _ModuleNotFoundError = ModuleNotFoundError
+except:
+    _ModuleNotFoundError = ImportError
+
+
+@pytest.fixture
+def background_json(tmpdir):
+    return {
+        "context": {
+            "eventId": "some-eventId",
+            "timestamp": "some-timestamp",
+            "eventType": "some-eventType",
+            "resource": "some-resource",
+        },
+        "data": {"filename": str(tmpdir / "filename.txt"), "value": "some-value"},
+    }
+
+
+def test_non_legacy_event_fails():
+    cloudevent = (
+        cloudevents.sdk.event.v1.Event()
+        .SetContentType("application/json")
+        .SetData('{"name":"john"}')
+        .SetEventID("my-id")
+        .SetSource("from-galaxy-far-far-away")
+        .SetEventTime("tomorrow")
+        .SetEventType("cloudevent.greet.you")
+    )
+    m = cloudevents.sdk.marshaller.NewDefaultHTTPMarshaller()
+    structured_headers, structured_data = m.ToRequest(
+        cloudevent, cloudevents.sdk.converters.TypeStructured, json.dumps
+    )
+
+    source = TEST_FUNCTIONS_DIR / "background_trigger" / "main.py"
+    target = "function"
+
+    client = create_app(target, source, "event").test_client()
+    resp = client.post("/", headers=structured_headers, data=structured_data.getvalue())
+    assert resp.status_code == 400
+    assert resp.data != b"OK"
+
+
+def test_background_function_executes(background_json):
+    source = TEST_FUNCTIONS_DIR / "background_trigger" / "main.py"
+    target = "function"
+
+    client = create_app(target, source, "event").test_client()
+
+    resp = client.post("/", json=background_json)
+    assert resp.status_code == 200
+
+
+def test_background_function_supports_get(background_json):
+    source = TEST_FUNCTIONS_DIR / "background_trigger" / "main.py"
+    target = "function"
+
+    client = create_app(target, source, "event").test_client()
+
+    resp = client.get("/")
+    assert resp.status_code == 200
+
+
+def test_background_function_executes_entry_point_one(background_json):
+    source = TEST_FUNCTIONS_DIR / "background_multiple_entry_points" / "main.py"
+    target = "myFunctionFoo"
+
+    client = create_app(target, source, "event").test_client()
+
+    resp = client.post("/", json=background_json)
+    assert resp.status_code == 200
+
+
+def test_background_function_executes_entry_point_two(background_json):
+    source = TEST_FUNCTIONS_DIR / "background_multiple_entry_points" / "main.py"
+    target = "myFunctionBar"
+
+    client = create_app(target, source, "event").test_client()
+
+    resp = client.post("/", json=background_json)
+    assert resp.status_code == 200
+
+
+def test_multiple_calls(background_json):
+    source = TEST_FUNCTIONS_DIR / "background_multiple_entry_points" / "main.py"
+    target = "myFunctionFoo"
+
+    client = create_app(target, source, "event").test_client()
+
+    resp = client.post("/", json=background_json)
+    assert resp.status_code == 200
+    resp = client.post("/", json=background_json)
+    assert resp.status_code == 200
+    resp = client.post("/", json=background_json)
+    assert resp.status_code == 200
+
+
+def test_pubsub_payload(background_json):
+    source = TEST_FUNCTIONS_DIR / "background_trigger" / "main.py"
+    target = "function"
+
+    client = create_app(target, source, "event").test_client()
+
+    resp = client.post("/", json=background_json)
+
+    assert resp.status_code == 200
+    assert resp.data == b"OK"
+
+    with open(background_json["data"]["filename"]) as f:
+        assert f.read() == '{{"entryPoint": "function", "value": "{}"}}'.format(
+            background_json["data"]["value"]
+        )
+
+
+def test_background_function_no_data(background_json):
+    source = TEST_FUNCTIONS_DIR / "background_trigger" / "main.py"
+    target = "function"
+
+    client = create_app(target, source, "event").test_client()
+
+    resp = client.post("/")
+    assert resp.status_code == 400
+
+
+def test_invalid_function_definition_multiple_entry_points():
+    source = TEST_FUNCTIONS_DIR / "background_multiple_entry_points" / "main.py"
+    target = "function"
+
+    with pytest.raises(exceptions.MissingTargetException) as excinfo:
+        create_app(target, source, "event")
+
+    assert re.match(
+        "File .* is expected to contain a function named function", str(excinfo.value)
+    )
+
+
+def test_invalid_function_definition_multiple_entry_points_invalid_function():
+    source = TEST_FUNCTIONS_DIR / "background_multiple_entry_points" / "main.py"
+    target = "invalidFunction"
+
+    with pytest.raises(exceptions.MissingTargetException) as excinfo:
+        create_app(target, source, "event")
+
+    assert re.match(
+        "File .* is expected to contain a function named invalidFunction",
+        str(excinfo.value),
+    )
+
+
+def test_invalid_function_definition_multiple_entry_points_not_a_function():
+    source = TEST_FUNCTIONS_DIR / "background_multiple_entry_points" / "main.py"
+    target = "notAFunction"
+
+    with pytest.raises(exceptions.InvalidTargetTypeException) as excinfo:
+        create_app(target, source, "event")
+
+    assert re.match(
+        "The function defined in file .* as notAFunction needs to be of type "
+        "function. Got: .*",
+        str(excinfo.value),
+    )
+
+
+def test_invalid_function_definition_function_syntax_error():
+    source = TEST_FUNCTIONS_DIR / "background_load_error" / "main.py"
+    target = "function"
+
+    with pytest.raises(SyntaxError) as excinfo:
+        create_app(target, source, "event")
+
+    assert any(
+        (
+            "invalid syntax" in str(excinfo.value),  # Python <3.8
+            "unmatched ')'" in str(excinfo.value),  # Python >3.8
+        )
+    )
+
+
+def test_invalid_function_definition_missing_dependency():
+    source = TEST_FUNCTIONS_DIR / "background_missing_dependency" / "main.py"
+    target = "function"
+
+    with pytest.raises(_ModuleNotFoundError) as excinfo:
+        create_app(target, source, "event")
+
+    assert "No module named 'nonexistentpackage'" in str(excinfo.value)
diff --git a/tests/test_functions.py b/tests/test_functions.py
index c6eccb91..792a646e 100644
--- a/tests/test_functions.py
+++ b/tests/test_functions.py
@@ -11,7 +11,6 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
 import pathlib
 import re
 import time
@@ -23,8 +22,7 @@
 
 from functions_framework import LazyWSGIApp, create_app, exceptions
 
-TEST_FUNCTIONS_DIR = pathlib.Path.cwd() / "tests" / "test_functions"
-
+TEST_FUNCTIONS_DIR = pathlib.Path(__file__).resolve().parent / "test_functions"
 
 # Python 3.5: ModuleNotFoundError does not exist
 try:
@@ -169,87 +167,6 @@ def test_http_function_execution_time():
     assert resp.data == b"OK"
 
 
-def test_background_function_executes(background_json):
-    source = TEST_FUNCTIONS_DIR / "background_trigger" / "main.py"
-    target = "function"
-
-    client = create_app(target, source, "event").test_client()
-
-    resp = client.post("/", json=background_json)
-    assert resp.status_code == 200
-
-
-def test_background_function_supports_get(background_json):
-    source = TEST_FUNCTIONS_DIR / "background_trigger" / "main.py"
-    target = "function"
-
-    client = create_app(target, source, "event").test_client()
-
-    resp = client.get("/")
-    assert resp.status_code == 200
-
-
-def test_background_function_executes_entry_point_one(background_json):
-    source = TEST_FUNCTIONS_DIR / "background_multiple_entry_points" / "main.py"
-    target = "myFunctionFoo"
-
-    client = create_app(target, source, "event").test_client()
-
-    resp = client.post("/", json=background_json)
-    assert resp.status_code == 200
-
-
-def test_background_function_executes_entry_point_two(background_json):
-    source = TEST_FUNCTIONS_DIR / "background_multiple_entry_points" / "main.py"
-    target = "myFunctionBar"
-
-    client = create_app(target, source, "event").test_client()
-
-    resp = client.post("/", json=background_json)
-    assert resp.status_code == 200
-
-
-def test_multiple_calls(background_json):
-    source = TEST_FUNCTIONS_DIR / "background_multiple_entry_points" / "main.py"
-    target = "myFunctionFoo"
-
-    client = create_app(target, source, "event").test_client()
-
-    resp = client.post("/", json=background_json)
-    assert resp.status_code == 200
-    resp = client.post("/", json=background_json)
-    assert resp.status_code == 200
-    resp = client.post("/", json=background_json)
-    assert resp.status_code == 200
-
-
-def test_pubsub_payload(background_json):
-    source = TEST_FUNCTIONS_DIR / "background_trigger" / "main.py"
-    target = "function"
-
-    client = create_app(target, source, "event").test_client()
-
-    resp = client.post("/", json=background_json)
-
-    assert resp.status_code == 200
-    assert resp.data == b"OK"
-
-    with open(background_json["data"]["filename"]) as f:
-        assert f.read() == '{{"entryPoint": "function", "value": "{}"}}'.format(
-            background_json["data"]["value"]
-        )
-
-
-def test_background_function_no_data(background_json):
-    source = TEST_FUNCTIONS_DIR / "background_trigger" / "main.py"
-    target = "function"
-
-    client = create_app(target, source, "event").test_client()
-
-    resp = client.post("/")
-    assert resp.status_code == 400
-
-
 def test_invalid_function_definition_missing_function_file():
     source = TEST_FUNCTIONS_DIR / "missing_function_file" / "main.py"
     target = "functions"
@@ -262,70 +179,6 @@ def test_invalid_function_definition_missing_function_file():
     )
 
 
-def test_invalid_function_definition_multiple_entry_points():
-    source = TEST_FUNCTIONS_DIR / "background_multiple_entry_points" / "main.py"
-    target = "function"
-
-    with pytest.raises(exceptions.MissingTargetException) as excinfo:
-        create_app(target, source, "event")
-
-    assert re.match(
-        "File .* is expected to contain a function named function", str(excinfo.value)
-    )
-
-
-def test_invalid_function_definition_multiple_entry_points_invalid_function():
-    source = TEST_FUNCTIONS_DIR / "background_multiple_entry_points" / "main.py"
-    target = "invalidFunction"
-
-    with pytest.raises(exceptions.MissingTargetException) as excinfo:
-        create_app(target, source, "event")
-
-    assert re.match(
-        "File .* is expected to contain a function named invalidFunction",
-        str(excinfo.value),
-    )
-
-
-def test_invalid_function_definition_multiple_entry_points_not_a_function():
-    source = TEST_FUNCTIONS_DIR / "background_multiple_entry_points" / "main.py"
-    target = "notAFunction"
-
-    with pytest.raises(exceptions.InvalidTargetTypeException) as excinfo:
-        create_app(target, source, "event")
-
-    assert re.match(
-        "The function defined in file .* as notAFunction needs to be of type "
-        "function. Got: .*",
-        str(excinfo.value),
-    )
-
-
-def test_invalid_function_definition_function_syntax_error():
-    source = TEST_FUNCTIONS_DIR / "background_load_error" / "main.py"
-    target = "function"
-
-    with pytest.raises(SyntaxError) as excinfo:
-        create_app(target, source, "event")
-
-    assert any(
-        (
-            "invalid syntax" in str(excinfo.value),  # Python <3.8
-            "unmatched ')'" in str(excinfo.value),  # Python >3.8
-        )
-    )
-
-
-def test_invalid_function_definition_missing_dependency():
-    source = TEST_FUNCTIONS_DIR / "background_missing_dependency" / "main.py"
-    target = "function"
-
-    with pytest.raises(_ModuleNotFoundError) as excinfo:
-        create_app(target, source, "event")
-
-    assert "No module named 'nonexistentpackage'" in str(excinfo.value)
-
-
 def test_invalid_configuration():
     with pytest.raises(exceptions.InvalidConfigurationException) as excinfo:
         create_app(None, None, None)
diff --git a/tests/test_functions/cloudevents/main.py b/tests/test_functions/cloudevents/main.py
new file mode 100644
index 00000000..f2fdb6f3
--- /dev/null
+++ b/tests/test_functions/cloudevents/main.py
@@ -0,0 +1,40 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Function used to test handling Cloud Event functions."""
+import flask
+
+
+def function(cloudevent):
+    """Test Event function that checks to see if a valid CloudEvent was sent.
+
+  The function returns 200 if it received the expected event, otherwise 500.
+
+  Args:
+    cloudevent: A Cloud event as defined by https://github.com/cloudevents/sdk-python.
+
+  Returns:
+    HTTP status code indicating whether valid event was sent or not.
+
+  """
+    valid_event = (
+        cloudevent.EventID() == "my-id"
+        and cloudevent.Data() == '{"name":"john"}'
+        and cloudevent.Source() == "from-galaxy-far-far-away"
+        and cloudevent.EventTime() == "tomorrow"
+        and cloudevent.EventType() == "cloudevent.greet.you"
+    )
+
+    if not valid_event:
+        flask.abort(500)
diff --git a/tests/test_view_functions.py b/tests/test_view_functions.py
index 51dad087..a9e13bb7 100644
--- a/tests/test_view_functions.py
+++ b/tests/test_view_functions.py
@@ -60,41 +60,6 @@ def test_event_view_func_wrapper(monkeypatch):
     ]
 
 
-def test_binary_event_view_func_wrapper(monkeypatch):
-    data = pretend.stub()
-    request = pretend.stub(
-        headers={
-            "ce-type": "something",
-            "ce-specversion": "something",
-            "ce-source": "something",
-            "ce-id": "something",
-            "ce-eventId": "some-eventId",
-            "ce-timestamp": "some-timestamp",
-            "ce-eventType": "some-eventType",
-            "ce-resource": "some-resource",
-        },
-        get_data=lambda: data,
-    )
-
-    context_stub = pretend.stub()
-    context_class = pretend.call_recorder(lambda *a, **kw: context_stub)
-    monkeypatch.setattr(functions_framework, "Context", context_class)
-    function = pretend.call_recorder(lambda data, context: "Hello")
-
-    view_func = functions_framework._event_view_func_wrapper(function, request)
-    view_func("/some/path")
-
-    assert function.calls == [pretend.call(data, context_stub)]
-    assert context_class.calls == [
-        pretend.call(
-            eventId="some-eventId",
-            timestamp="some-timestamp",
-            eventType="some-eventType",
-            resource="some-resource",
-        )
-    ]
-
-
 def test_legacy_event_view_func_wrapper(monkeypatch):
     data = pretend.stub()
     json = {