Repository: googleapis/google-cloud-python

Issue:
Bigtable: Instance methods should use 'Client.instance_admin_client' property
005ddd11770f8953643dac9b64a037524da2f49e added three API-invoking methods to `Instance`:

- `get_iam_policy`
- `set_iam_policy`
- `test_iam_permissions`

The implementations of those methods use the client's `_instance_admin_client` attribute, which may not have been initialized:  instead, they should use the `instance_admin_client` property.


Discussion:

Resolving patch:
diff --git a/bigtable/docs/snippets.py b/bigtable/docs/snippets.py
--- a/bigtable/docs/snippets.py
+++ b/bigtable/docs/snippets.py
@@ -58,11 +58,12 @@ class Config(object):
     This is a mutable stand-in to allow test set-up to modify
     global state.
     """
+    CLIENT = None
     INSTANCE = None
 
 
 def setup_module():
-    client = Client(admin=True)
+    client = Config.CLIENT = Client(admin=True)
     Config.INSTANCE = client.instance(INSTANCE_ID,
                                       instance_type=PRODUCTION,
                                       labels=LABELS)
@@ -337,6 +338,7 @@ def test_bigtable_delete_instance():
     client = Client(admin=True)
     instance_id_to_delete = "inst-my-" + unique_resource_id('-')
     # [END bigtable_delete_instance]
+
     cluster_id = "clus-my-" + unique_resource_id('-')
 
     instance = client.instance(instance_id_to_delete,
@@ -354,6 +356,7 @@ def test_bigtable_delete_instance():
     instance_to_delete = client.instance(instance_id_to_delete)
     instance_to_delete.delete()
     # [END bigtable_delete_instance]
+
     assert not instance_to_delete.exists()
 
 
@@ -367,40 +370,44 @@ def test_bigtable_test_iam_permissions():
     permissions = ["bigtable.clusters.create", "bigtable.tables.create"]
     permissions_allowed = instance.test_iam_permissions(permissions)
     # [END bigtable_test_iam_permissions]
-    assert permissions_allowed == permissions
 
-
-def test_bigtable_get_iam_policy():
-    # [START bigtable_get_iam_policy]
-    from google.cloud.bigtable import Client
-
-    client = Client(admin=True)
-    instance = client.instance(INSTANCE_ID)
-    policy_latest = instance.get_iam_policy()
-    # [END bigtable_get_iam_policy]
-
-    assert len(policy_latest.bigtable_viewers) is not 0
+    assert permissions_allowed == permissions
 
 
-def test_bigtable_set_iam_policy():
+def test_bigtable_set_iam_policy_then_get_iam_policy():
     # [START bigtable_set_iam_policy]
     from google.cloud.bigtable import Client
     from google.cloud.bigtable.policy import Policy
     from google.cloud.bigtable.policy import BIGTABLE_ADMIN_ROLE
 
+    # [END bigtable_set_iam_policy]
+
+    service_account_email = Config.CLIENT._credentials.service_account_email
+
+    # [START bigtable_set_iam_policy]
     client = Client(admin=True)
     instance = client.instance(INSTANCE_ID)
     instance.reload()
-    ins_policy = Policy()
-    ins_policy[BIGTABLE_ADMIN_ROLE] = [
-        Policy.user("test_iam@test.com"),
-        Policy.service_account("sv_account@gmail.com")]
+    new_policy = Policy()
+    new_policy[BIGTABLE_ADMIN_ROLE] = [
+        Policy.service_account(service_account_email),
+    ]
 
-    policy_latest = instance.set_iam_policy(ins_policy)
+    policy_latest = instance.set_iam_policy(new_policy)
     # [END bigtable_set_iam_policy]
 
     assert len(policy_latest.bigtable_admins) is not 0
 
+    # [START bigtable_get_iam_policy]
+    from google.cloud.bigtable import Client
+
+    client = Client(admin=True)
+    instance = client.instance(INSTANCE_ID)
+    policy = instance.get_iam_policy()
+    # [END bigtable_get_iam_policy]
+
+    assert len(policy.bigtable_admins) is not 0
+
 
 if __name__ == '__main__':
     pytest.main()
diff --git a/bigtable/google/cloud/bigtable/instance.py b/bigtable/google/cloud/bigtable/instance.py
--- a/bigtable/google/cloud/bigtable/instance.py
+++ b/bigtable/google/cloud/bigtable/instance.py
@@ -106,6 +106,17 @@ def __init__(self,
         self.labels = labels
         self._state = _state
 
+    def _update_from_pb(self, instance_pb):
+        """Refresh self from the server-provided protobuf.
+        Helper for :meth:`from_pb` and :meth:`reload`.
+        """
+        if not instance_pb.display_name:  # Simple field (string)
+            raise ValueError('Instance protobuf does not contain display_name')
+        self.display_name = instance_pb.display_name
+        self.type_ = instance_pb.type
+        self.labels = dict(instance_pb.labels)
+        self._state = instance_pb.state
+
     @classmethod
     def from_pb(cls, instance_pb, client):
         """Creates an instance instance from a protobuf.
@@ -137,17 +148,6 @@ def from_pb(cls, instance_pb, client):
         result._update_from_pb(instance_pb)
         return result
 
-    def _update_from_pb(self, instance_pb):
-        """Refresh self from the server-provided protobuf.
-        Helper for :meth:`from_pb` and :meth:`reload`.
-        """
-        if not instance_pb.display_name:  # Simple field (string)
-            raise ValueError('Instance protobuf does not contain display_name')
-        self.display_name = instance_pb.display_name
-        self.type_ = instance_pb.type
-        self.labels = dict(instance_pb.labels)
-        self._state = instance_pb.state
-
     @property
     def name(self):
         """Instance name used in requests.
@@ -186,41 +186,6 @@ def __eq__(self, other):
     def __ne__(self, other):
         return not self == other
 
-    def reload(self):
-        """Reload the metadata for this instance.
-
-        For example:
-
-        .. literalinclude:: snippets.py
-            :start-after: [START bigtable_reload_instance]
-            :end-before: [END bigtable_reload_instance]
-        """
-        instance_pb = self._client.instance_admin_client.get_instance(
-            self.name)
-
-        # NOTE: _update_from_pb does not check that the project and
-        #       instance ID on the response match the request.
-        self._update_from_pb(instance_pb)
-
-    def exists(self):
-        """Check whether the instance already exists.
-
-        For example:
-
-        .. literalinclude:: snippets.py
-            :start-after: [START bigtable_check_instance_exists]
-            :end-before: [END bigtable_check_instance_exists]
-
-        :rtype: bool
-        :returns: True if the table exists, else False.
-        """
-        try:
-            self._client.instance_admin_client.get_instance(name=self.name)
-            return True
-        # NOTE: There could be other exceptions that are returned to the user.
-        except NotFound:
-            return False
-
     def create(self, location_id=None,
                serve_nodes=None,
                default_storage_type=None, clusters=None):
@@ -303,6 +268,41 @@ def create(self, location_id=None,
             parent=parent, instance_id=self.instance_id, instance=instance_pb,
             clusters={c.cluster_id: c._to_pb() for c in clusters})
 
+    def exists(self):
+        """Check whether the instance already exists.
+
+        For example:
+
+        .. literalinclude:: snippets.py
+            :start-after: [START bigtable_check_instance_exists]
+            :end-before: [END bigtable_check_instance_exists]
+
+        :rtype: bool
+        :returns: True if the table exists, else False.
+        """
+        try:
+            self._client.instance_admin_client.get_instance(name=self.name)
+            return True
+        # NOTE: There could be other exceptions that are returned to the user.
+        except NotFound:
+            return False
+
+    def reload(self):
+        """Reload the metadata for this instance.
+
+        For example:
+
+        .. literalinclude:: snippets.py
+            :start-after: [START bigtable_reload_instance]
+            :end-before: [END bigtable_reload_instance]
+        """
+        instance_pb = self._client.instance_admin_client.get_instance(
+            self.name)
+
+        # NOTE: _update_from_pb does not check that the project and
+        #       instance ID on the response match the request.
+        self._update_from_pb(instance_pb)
+
     def update(self):
         """Updates an instance within a project.
 
@@ -376,6 +376,87 @@ def delete(self):
         """
         self._client.instance_admin_client.delete_instance(name=self.name)
 
+    def get_iam_policy(self):
+        """Gets the access control policy for an instance resource.
+
+        For example:
+
+        .. literalinclude:: snippets.py
+            :start-after: [START bigtable_get_iam_policy]
+            :end-before: [END bigtable_get_iam_policy]
+
+        :rtype: :class:`google.cloud.bigtable.policy.Policy`
+        :returns: The current IAM policy of this instance
+        """
+        instance_admin_client = self._client.instance_admin_client
+        resp = instance_admin_client.get_iam_policy(resource=self.name)
+        return Policy.from_api_repr(self._to_dict_from_policy_pb(resp))
+
+    def set_iam_policy(self, policy):
+        """Sets the access control policy on an instance resource. Replaces any
+        existing policy.
+
+        For more information about policy, please see documentation of
+        class `google.cloud.bigtable.policy.Policy`
+
+        For example:
+
+        .. literalinclude:: snippets.py
+            :start-after: [START bigtable_set_iam_policy]
+            :end-before: [END bigtable_set_iam_policy]
+
+        :type policy: :class:`google.cloud.bigtable.policy.Policy`
+        :param policy: A new IAM policy to replace the current IAM policy
+                       of this instance
+
+        :rtype: :class:`google.cloud.bigtable.policy.Policy`
+        :returns: The current IAM policy of this instance.
+        """
+        instance_admin_client = self._client.instance_admin_client
+        resp = instance_admin_client.set_iam_policy(
+            resource=self.name, policy=policy.to_api_repr())
+        return Policy.from_api_repr(self._to_dict_from_policy_pb(resp))
+
+    def test_iam_permissions(self, permissions):
+        """Returns permissions that the caller has on the specified instance
+        resource.
+
+        For example:
+
+        .. literalinclude:: snippets.py
+            :start-after: [START bigtable_test_iam_permissions]
+            :end-before: [END bigtable_test_iam_permissions]
+
+        :type permissions: list
+        :param permissions: The set of permissions to check for
+               the ``resource``. Permissions with wildcards (such as '*'
+               or 'storage.*') are not allowed. For more information see
+               `IAM Overview
+               <https://cloud.google.com/iam/docs/overview#permissions>`_.
+               `Bigtable Permissions
+               <https://cloud.google.com/bigtable/docs/access-control>`_.
+
+        :rtype: list
+        :returns: A List(string) of permissions allowed on the instance
+        """
+        instance_admin_client = self._client.instance_admin_client
+        resp = instance_admin_client.test_iam_permissions(
+            resource=self.name, permissions=permissions)
+        return list(resp.permissions)
+
+    def _to_dict_from_policy_pb(self, policy):
+        """Returns a dictionary representation of resource returned from
+        the getIamPolicy API to use as parameter for
+        :meth: google.cloud.iam.Policy.from_api_repr
+        """
+        pb_dict = {}
+        bindings = [{'role': binding.role, 'members': binding.members}
+                    for binding in policy.bindings]
+        pb_dict['etag'] = policy.etag
+        pb_dict['version'] = policy.version
+        pb_dict['bindings'] = bindings
+        return pb_dict
+
     def cluster(self, cluster_id, location_id=None,
                 serve_nodes=None, default_storage_type=None):
         """Factory to create a cluster associated with this instance.
@@ -548,86 +629,5 @@ def list_app_profiles(self):
                   :class:`~google.cloud.bigtable.app_profile.AppProfile`
                   instances.
         """
-        resp = self._client._instance_admin_client.list_app_profiles(self.name)
+        resp = self._client.instance_admin_client.list_app_profiles(self.name)
         return [AppProfile.from_pb(app_profile, self) for app_profile in resp]
-
-    def get_iam_policy(self):
-        """Gets the access control policy for an instance resource.
-
-        For example:
-
-        .. literalinclude:: snippets.py
-            :start-after: [START bigtable_get_iam_policy]
-            :end-before: [END bigtable_get_iam_policy]
-
-        :rtype: :class:`google.cloud.bigtable.policy.Policy`
-        :returns: The current IAM policy of this instance
-        """
-        instance_admin_client = self._client._instance_admin_client
-        resp = instance_admin_client.get_iam_policy(resource=self.name)
-        return Policy.from_api_repr(self._to_dict_from_policy_pb(resp))
-
-    def set_iam_policy(self, policy):
-        """Sets the access control policy on an instance resource. Replaces any
-        existing policy.
-
-        For more information about policy, please see documentation of
-        class `google.cloud.bigtable.policy.Policy`
-
-        For example:
-
-        .. literalinclude:: snippets.py
-            :start-after: [START bigtable_set_iam_policy]
-            :end-before: [END bigtable_set_iam_policy]
-
-        :type policy: :class:`google.cloud.bigtable.policy.Policy`
-        :param policy: A new IAM policy to replace the current IAM policy
-                       of this instance
-
-        :rtype: :class:`google.cloud.bigtable.policy.Policy`
-        :returns: The current IAM policy of this instance.
-        """
-        instance_admin_client = self._client._instance_admin_client
-        resp = instance_admin_client.set_iam_policy(
-            resource=self.name, policy=policy.to_api_repr())
-        return Policy.from_api_repr(self._to_dict_from_policy_pb(resp))
-
-    def test_iam_permissions(self, permissions):
-        """Returns permissions that the caller has on the specified instance
-        resource.
-
-        For example:
-
-        .. literalinclude:: snippets.py
-            :start-after: [START bigtable_test_iam_permissions]
-            :end-before: [END bigtable_test_iam_permissions]
-
-        :type permissions: list
-        :param permissions: The set of permissions to check for
-               the ``resource``. Permissions with wildcards (such as '*'
-               or 'storage.*') are not allowed. For more information see
-               `IAM Overview
-               <https://cloud.google.com/iam/docs/overview#permissions>`_.
-               `Bigtable Permissions
-               <https://cloud.google.com/bigtable/docs/access-control>`_.
-
-        :rtype: list
-        :returns: A List(string) of permissions allowed on the instance
-        """
-        instance_admin_client = self._client._instance_admin_client
-        resp = instance_admin_client.test_iam_permissions(
-            resource=self.name, permissions=permissions)
-        return list(resp.permissions)
-
-    def _to_dict_from_policy_pb(self, policy):
-        """Returns a dictionary representation of resource returned from
-        the getIamPolicy API to use as parameter for
-        :meth: google.cloud.iam.Policy.from_api_repr
-        """
-        pb_dict = {}
-        bindings = [{'role': binding.role, 'members': binding.members}
-                    for binding in policy.bindings]
-        pb_dict['etag'] = policy.etag
-        pb_dict['version'] = policy.version
-        pb_dict['bindings'] = bindings
-        return pb_dict
diff --git a/bigtable/noxfile.py b/bigtable/noxfile.py
--- a/bigtable/noxfile.py
+++ b/bigtable/noxfile.py
@@ -115,7 +115,7 @@ def cover(session):
     session.run('coverage', 'erase')
 
 
-@nox.session(python='3.7')
+@nox.session(python=['2.7', '3.7'])
 def snippets(session):
     """Run the system test suite."""
     # Sanity check: Only run system tests if the environment variable is set.
@@ -126,9 +126,11 @@ def snippets(session):
     session.install('mock', 'pytest')
     for local_dep in LOCAL_DEPS:
         session.install('-e', local_dep)
-    session.install('-e', os.path.join('..', 'bigtable'))
     session.install('-e', '../test_utils/')
     session.install('-e', '.')
-    session.run('py.test', '--quiet', \
-                os.path.join('docs', 'snippets.py'), \
-                *session.posargs)
+    session.run(
+        'py.test',
+        '--quiet',
+        os.path.join('docs', 'snippets.py'),
+        *session.posargs,
+    )


Test patch:
diff --git a/bigtable/tests/unit/test_instance.py b/bigtable/tests/unit/test_instance.py
--- a/bigtable/tests/unit/test_instance.py
+++ b/bigtable/tests/unit/test_instance.py
@@ -21,33 +21,6 @@
 from google.cloud.bigtable.cluster import Cluster
 
 
-class MultiCallableStub(object):
-    """Stub for the grpc.UnaryUnaryMultiCallable interface."""
-
-    def __init__(self, method, channel_stub):
-        self.method = method
-        self.channel_stub = channel_stub
-
-    def __call__(self, request, timeout=None, metadata=None, credentials=None):
-        self.channel_stub.requests.append((self.method, request))
-
-        return self.channel_stub.responses.pop()
-
-
-class ChannelStub(object):
-    """Stub for the grpc.Channel interface."""
-
-    def __init__(self, responses=[]):
-        self.responses = responses
-        self.requests = []
-
-    def unary_unary(self,
-                    method,
-                    request_serializer=None,
-                    response_deserializer=None):
-        return MultiCallableStub(method, self)
-
-
 class TestInstance(unittest.TestCase):
 
     PROJECT = 'project'
@@ -114,98 +87,6 @@ def test_constructor_non_default(self):
         self.assertIs(instance._client, client)
         self.assertEqual(instance.state, state)
 
-    def test_table_factory(self):
-        from google.cloud.bigtable.table import Table
-
-        app_profile_id = 'appProfileId1262094415'
-        instance = self._make_one(self.INSTANCE_ID, None)
-
-        table = instance.table(self.TABLE_ID, app_profile_id=app_profile_id)
-        self.assertIsInstance(table, Table)
-        self.assertEqual(table.table_id, self.TABLE_ID)
-        self.assertEqual(table._instance, instance)
-        self.assertEqual(table._app_profile_id, app_profile_id)
-
-    def test_cluster_factory(self):
-        from google.cloud.bigtable import enums
-
-        CLUSTER_ID = '{}-cluster'.format(self.INSTANCE_ID)
-        LOCATION_ID = 'us-central1-c'
-        SERVE_NODES = 3
-        STORAGE_TYPE = enums.StorageType.HDD
-
-        instance = self._make_one(self.INSTANCE_ID, None)
-
-        cluster = instance.cluster(CLUSTER_ID, location_id=LOCATION_ID,
-                                   serve_nodes=SERVE_NODES,
-                                   default_storage_type=STORAGE_TYPE)
-        self.assertIsInstance(cluster, Cluster)
-        self.assertEqual(cluster.cluster_id, CLUSTER_ID)
-        self.assertEqual(cluster.location_id, LOCATION_ID)
-        self.assertIsNone(cluster._state)
-        self.assertEqual(cluster.serve_nodes, SERVE_NODES)
-        self.assertEqual(cluster.default_storage_type, STORAGE_TYPE)
-
-    def test_list_clusters(self):
-        from google.cloud.bigtable_admin_v2.gapic import (
-            bigtable_instance_admin_client)
-        from google.cloud.bigtable_admin_v2.proto import (
-            bigtable_instance_admin_pb2 as messages_v2_pb2)
-        from google.cloud.bigtable_admin_v2.proto import (
-            instance_pb2 as data_v2_pb2)
-        from google.cloud.bigtable.instance import Instance
-        from google.cloud.bigtable.instance import Cluster
-
-        instance_api = (
-            bigtable_instance_admin_client.BigtableInstanceAdminClient(
-                mock.Mock()))
-        credentials = _make_credentials()
-        client = self._make_client(project=self.PROJECT,
-                                   credentials=credentials, admin=True)
-        instance = Instance(self.INSTANCE_ID, client)
-
-        failed_location = 'FAILED'
-        cluster_id1 = 'cluster-id1'
-        cluster_id2 = 'cluster-id2'
-        cluster_name1 = (client.instance_admin_client.cluster_path(
-                         self.PROJECT, self.INSTANCE_ID, cluster_id1))
-        cluster_name2 = (client.instance_admin_client.cluster_path(
-                         self.PROJECT, self.INSTANCE_ID, cluster_id2))
-
-        # Create response_pb
-        response_pb = messages_v2_pb2.ListClustersResponse(
-            failed_locations=[
-                failed_location
-            ],
-            clusters=[
-                data_v2_pb2.Cluster(
-                    name=cluster_name1,
-                ),
-                data_v2_pb2.Cluster(
-                    name=cluster_name2,
-                ),
-            ],
-        )
-
-        # Patch the stub used by the API method.
-        client._instance_admin_client = instance_api
-        instance_admin_client = client._instance_admin_client
-        instance_stub = instance_admin_client.transport
-        instance_stub.list_clusters.side_effect = [response_pb]
-
-        # Perform the method and check the result.
-        clusters, failed_locations = instance.list_clusters()
-
-        cluster_1, cluster_2 = clusters
-
-        self.assertIsInstance(cluster_1, Cluster)
-        self.assertEqual(cluster_1.name, cluster_name1)
-
-        self.assertIsInstance(cluster_2, Cluster)
-        self.assertEqual(cluster_2.name, cluster_name2)
-
-        self.assertEqual(failed_locations, [failed_location])
-
     def test__update_from_pb_success(self):
         from google.cloud.bigtable_admin_v2.proto import (
             instance_pb2 as data_v2_pb2)
@@ -264,8 +145,9 @@ def test_from_pb_success(self):
             instance_pb2 as data_v2_pb2)
         from google.cloud.bigtable import enums
 
-        client = _Client(project=self.PROJECT)
-
+        credentials = _make_credentials()
+        client = self._make_client(
+            project=self.PROJECT, credentials=credentials, admin=True)
         instance_type = enums.Instance.Type.PRODUCTION
         state = enums.Instance.State.READY
         instance_pb = data_v2_pb2.Instance(
@@ -302,7 +184,9 @@ def test_from_pb_project_mistmatch(self):
             instance_pb2 as data_v2_pb2)
 
         ALT_PROJECT = 'ALT_PROJECT'
-        client = _Client(project=ALT_PROJECT)
+        credentials = _make_credentials()
+        client = self._make_client(
+            project=ALT_PROJECT, credentials=credentials, admin=True)
 
         self.assertNotEqual(self.PROJECT, ALT_PROJECT)
 
@@ -352,47 +236,172 @@ def test___ne__(self):
         instance2 = self._make_one('instance_id2', 'client2')
         self.assertNotEqual(instance1, instance2)
 
-    def test_reload(self):
-        from google.cloud.bigtable_admin_v2.proto import (
-            instance_pb2 as data_v2_pb2)
+    def test_create_check_location_and_clusters(self):
+        instance = self._make_one(self.INSTANCE_ID, None)
+
+        with self.assertRaises(ValueError):
+            instance.create(
+                location_id=self.LOCATION_ID, clusters=[object(), object()])
+
+    def test_create_check_serve_nodes_and_clusters(self):
+        instance = self._make_one(self.INSTANCE_ID, None)
+
+        with self.assertRaises(ValueError):
+            instance.create(
+                serve_nodes=3, clusters=[object(), object()])
+
+    def test_create_check_default_storage_type_and_clusters(self):
+        instance = self._make_one(self.INSTANCE_ID, None)
+
+        with self.assertRaises(ValueError):
+            instance.create(
+                default_storage_type=1, clusters=[object(), object()])
+
+    def _instance_api_response_for_create(self):
+        import datetime
+        from google.api_core import operation
+        from google.longrunning import operations_pb2
+        from google.protobuf.any_pb2 import Any
+        from google.cloud._helpers import _datetime_to_pb_timestamp
         from google.cloud.bigtable_admin_v2.gapic import (
             bigtable_instance_admin_client)
+        from google.cloud.bigtable_admin_v2.proto import (
+            bigtable_instance_admin_pb2 as messages_v2_pb2)
+        from google.cloud.bigtable_admin_v2.types import instance_pb2
+
+        NOW = datetime.datetime.utcnow()
+        NOW_PB = _datetime_to_pb_timestamp(NOW)
+        metadata = messages_v2_pb2.CreateInstanceMetadata(request_time=NOW_PB)
+        type_url = 'type.googleapis.com/{}'.format(
+            messages_v2_pb2.CreateInstanceMetadata.DESCRIPTOR.full_name)
+        response_pb = operations_pb2.Operation(
+            name=self.OP_NAME,
+            metadata=Any(
+                type_url=type_url,
+                value=metadata.SerializeToString(),
+            )
+        )
+        response = operation.from_gapic(
+            response_pb,
+            mock.Mock(),
+            instance_pb2.Instance,
+            metadata_type=messages_v2_pb2.CreateInstanceMetadata,
+        )
+        project_path_template = 'projects/{}'
+        location_path_template = 'projects/{}/locations/{}'
+        instance_api = mock.create_autospec(
+            bigtable_instance_admin_client.BigtableInstanceAdminClient)
+        instance_api.create_instance.return_value = response
+        instance_api.project_path = project_path_template.format
+        instance_api.location_path = location_path_template.format
+        return instance_api, response
+
+    def test_create(self):
         from google.cloud.bigtable import enums
+        from google.cloud.bigtable_admin_v2.types import instance_pb2
 
-        api = bigtable_instance_admin_client.BigtableInstanceAdminClient(
-            mock.Mock())
         credentials = _make_credentials()
-        client = self._make_client(project=self.PROJECT,
-                                   credentials=credentials, admin=True)
-        instance = self._make_one(self.INSTANCE_ID, client)
+        client = self._make_client(
+            project=self.PROJECT, credentials=credentials, admin=True)
+        instance = self._make_one(
+            self.INSTANCE_ID,
+            client,
+            self.DISPLAY_NAME,
+            enums.Instance.Type.PRODUCTION,
+            self.LABELS,
+        )
+        instance_api, response = self._instance_api_response_for_create()
+        client._instance_admin_client = instance_api
+        serve_nodes = 3
 
-        # Create response_pb
-        DISPLAY_NAME = u'hey-hi-hello'
-        instance_type = enums.Instance.Type.PRODUCTION
-        response_pb = data_v2_pb2.Instance(
-            display_name=DISPLAY_NAME,
-            type=instance_type,
-            labels=self.LABELS
+        result = instance.create(
+            location_id=self.LOCATION_ID,
+            serve_nodes=serve_nodes,
         )
 
-        # Patch the stub used by the API method.
-        client._instance_admin_client = api
-        bigtable_instance_stub = (
-            client._instance_admin_client.transport)
-        bigtable_instance_stub.get_instance.side_effect = [response_pb]
+        cluster_pb = instance_pb2.Cluster(
+            location=instance_api.location_path(
+                self.PROJECT, self.LOCATION_ID),
+            serve_nodes=serve_nodes,
+            default_storage_type=enums.StorageType.UNSPECIFIED,
+        )
+        instance_pb = instance_pb2.Instance(
+            display_name=self.DISPLAY_NAME,
+            type=enums.Instance.Type.PRODUCTION,
+            labels=self.LABELS,
+        )
+        cluster_id = '{}-cluster'.format(self.INSTANCE_ID)
+        instance_api.create_instance.assert_called_once_with(
+            parent=instance_api.project_path(self.PROJECT),
+            instance_id=self.INSTANCE_ID,
+            instance=instance_pb,
+            clusters={cluster_id: cluster_pb},
+        )
 
-        # Create expected_result.
-        expected_result = None  # reload() has no return value.
+        self.assertIs(result, response)
 
-        # Check Instance optional config values before.
-        self.assertEqual(instance.display_name, self.INSTANCE_ID)
+    def test_create_w_clusters(self):
+        from google.cloud.bigtable import enums
+        from google.cloud.bigtable_admin_v2.types import instance_pb2
+
+        credentials = _make_credentials()
+        client = self._make_client(
+            project=self.PROJECT, credentials=credentials, admin=True)
+        instance = self._make_one(
+            self.INSTANCE_ID,
+            client,
+            self.DISPLAY_NAME,
+            enums.Instance.Type.PRODUCTION,
+            self.LABELS,
+        )
+        instance_api, response = self._instance_api_response_for_create()
+        client._instance_admin_client = instance_api
 
         # Perform the method and check the result.
-        result = instance.reload()
-        self.assertEqual(result, expected_result)
+        cluster_id_1 = 'cluster-1'
+        cluster_id_2 = 'cluster-2'
+        location_id_1 = 'location-id-1'
+        location_id_2 = 'location-id-2'
+        serve_nodes_1 = 3
+        serve_nodes_2 = 5
+        clusters = [
+            Cluster(cluster_id_1, instance,
+                    location_id=location_id_1,
+                    serve_nodes=serve_nodes_1),
+            Cluster(cluster_id_2, instance,
+                    location_id=location_id_2,
+                    serve_nodes=serve_nodes_2)]
 
-        # Check Instance optional config values before.
-        self.assertEqual(instance.display_name, DISPLAY_NAME)
+        result = instance.create(clusters=clusters)
+
+        cluster_pb_1 = instance_pb2.Cluster(
+            location=instance_api.location_path(
+                self.PROJECT, location_id_1),
+            serve_nodes=serve_nodes_1,
+            default_storage_type=enums.StorageType.UNSPECIFIED,
+        )
+        cluster_pb_2 = instance_pb2.Cluster(
+            location=instance_api.location_path(
+                self.PROJECT, location_id_2),
+            serve_nodes=serve_nodes_2,
+            default_storage_type=enums.StorageType.UNSPECIFIED,
+        )
+        instance_pb = instance_pb2.Instance(
+            display_name=self.DISPLAY_NAME,
+            type=enums.Instance.Type.PRODUCTION,
+            labels=self.LABELS,
+        )
+        instance_api.create_instance.assert_called_once_with(
+            parent=instance_api.project_path(self.PROJECT),
+            instance_id=self.INSTANCE_ID,
+            instance=instance_pb,
+            clusters={
+                cluster_id_1: cluster_pb_1,
+                cluster_id_2: cluster_pb_2,
+            },
+        )
+
+        self.assertIs(result, response)
 
     def test_exists(self):
         from google.cloud.bigtable_admin_v2.gapic import (
@@ -429,113 +438,69 @@ def test_exists(self):
         alt_instance_2 = self._make_one(non_existing_instance_id, client)
         self.assertTrue(alt_instance_1.exists())
         self.assertFalse(alt_instance_2.exists())
+
         with self.assertRaises(exceptions.BadRequest):
             alt_instance_2.exists()
 
-    def test_create_check_conflicts(self):
-        instance = self._make_one(self.INSTANCE_ID, None)
-        with self.assertRaises(ValueError):
-            instance.create(location_id=self.LOCATION_ID,
-                            clusters=[object(), object()])
-        with self.assertRaises(ValueError):
-            instance.create(serve_nodes=3,
-                            clusters=[object(), object()])
-        with self.assertRaises(ValueError):
-            instance.create(default_storage_type=1,
-                            clusters=[object(), object()])
-
-    def test_create(self):
-        import datetime
-        from google.api_core import operation
-        from google.longrunning import operations_pb2
-        from google.protobuf.any_pb2 import Any
+    def test_reload(self):
         from google.cloud.bigtable_admin_v2.proto import (
-            bigtable_instance_admin_pb2 as messages_v2_pb2)
-        from google.cloud._helpers import _datetime_to_pb_timestamp
-        from google.cloud.bigtable import enums
+            instance_pb2 as data_v2_pb2)
         from google.cloud.bigtable_admin_v2.gapic import (
             bigtable_instance_admin_client)
+        from google.cloud.bigtable import enums
 
-        NOW = datetime.datetime.utcnow()
-        NOW_PB = _datetime_to_pb_timestamp(NOW)
+        api = bigtable_instance_admin_client.BigtableInstanceAdminClient(
+            mock.Mock())
         credentials = _make_credentials()
         client = self._make_client(project=self.PROJECT,
                                    credentials=credentials, admin=True)
-        instance = self._make_one(self.INSTANCE_ID, client,
-                                  self.DISPLAY_NAME,
-                                  enums.Instance.Type.PRODUCTION,
-                                  self.LABELS)
+        instance = self._make_one(self.INSTANCE_ID, client)
 
         # Create response_pb
-        metadata = messages_v2_pb2.CreateInstanceMetadata(request_time=NOW_PB)
-        type_url = 'type.googleapis.com/{}'.format(
-            messages_v2_pb2.CreateInstanceMetadata.DESCRIPTOR.full_name)
-        response_pb = operations_pb2.Operation(
-            name=self.OP_NAME,
-            metadata=Any(
-                type_url=type_url,
-                value=metadata.SerializeToString(),
-            )
+        DISPLAY_NAME = u'hey-hi-hello'
+        instance_type = enums.Instance.Type.PRODUCTION
+        response_pb = data_v2_pb2.Instance(
+            display_name=DISPLAY_NAME,
+            type=instance_type,
+            labels=self.LABELS
         )
 
         # Patch the stub used by the API method.
-        channel = ChannelStub(responses=[response_pb])
-        instance_api = (
-            bigtable_instance_admin_client.BigtableInstanceAdminClient(
-                channel=channel))
-        client._instance_admin_client = instance_api
+        client._instance_admin_client = api
+        bigtable_instance_stub = (
+            client._instance_admin_client.transport)
+        bigtable_instance_stub.get_instance.side_effect = [response_pb]
 
-        # Perform the method and check the result.
-        serve_nodes = 3
-        cluster_id = '{}-cluster'.format(self.INSTANCE_ID)
-        # cluster = instance.cluster(cluster_id, location_id=self.LOCATION_ID,
-        #                            serve_nodes=serve_nodes)
-        # result = instance.create(clusters=[cluster])
-
-        # TODO: replace this example with above once the otpion is removed
-        # from instance.create() method
-        result = instance.create(location_id=self.LOCATION_ID,
-                                 serve_nodes=serve_nodes)
+        # Create expected_result.
+        expected_result = None  # reload() has no return value.
 
-        actual_request = channel.requests[0][1]
+        # Check Instance optional config values before.
+        self.assertEqual(instance.display_name, self.INSTANCE_ID)
 
-        cluster = self._create_cluster_pb(
-            instance_api, cluster_id, self.LOCATION_ID, serve_nodes,
-            enums.StorageType.UNSPECIFIED)
+        # Perform the method and check the result.
+        result = instance.reload()
+        self.assertEqual(result, expected_result)
 
-        expected_request = self._create_instance_request({cluster_id: cluster})
-        self.assertEqual(expected_request, actual_request)
-        self.assertIsInstance(result, operation.Operation)
-        self.assertEqual(result.operation.name, self.OP_NAME)
-        self.assertIsInstance(result.metadata,
-                              messages_v2_pb2.CreateInstanceMetadata)
+        # Check Instance optional config values before.
+        self.assertEqual(instance.display_name, DISPLAY_NAME)
 
-    def test_create_w_clusters(self):
+    def _instance_api_response_for_update(self):
         import datetime
         from google.api_core import operation
         from google.longrunning import operations_pb2
         from google.protobuf.any_pb2 import Any
-        from google.cloud.bigtable_admin_v2.proto import (
-            bigtable_instance_admin_pb2 as messages_v2_pb2)
         from google.cloud._helpers import _datetime_to_pb_timestamp
-        from google.cloud.bigtable import enums
         from google.cloud.bigtable_admin_v2.gapic import (
             bigtable_instance_admin_client)
+        from google.cloud.bigtable_admin_v2.proto import (
+            bigtable_instance_admin_pb2 as messages_v2_pb2)
+        from google.cloud.bigtable_admin_v2.types import instance_pb2
 
         NOW = datetime.datetime.utcnow()
         NOW_PB = _datetime_to_pb_timestamp(NOW)
-        credentials = _make_credentials()
-        client = self._make_client(project=self.PROJECT,
-                                   credentials=credentials, admin=True)
-        instance = self._make_one(self.INSTANCE_ID, client,
-                                  self.DISPLAY_NAME,
-                                  enums.Instance.Type.PRODUCTION,
-                                  self.LABELS)
-
-        # Create response_pb
-        metadata = messages_v2_pb2.CreateInstanceMetadata(request_time=NOW_PB)
+        metadata = messages_v2_pb2.UpdateInstanceMetadata(request_time=NOW_PB)
         type_url = 'type.googleapis.com/{}'.format(
-            messages_v2_pb2.CreateInstanceMetadata.DESCRIPTOR.full_name)
+            messages_v2_pb2.UpdateInstanceMetadata.DESCRIPTOR.full_name)
         response_pb = operations_pb2.Operation(
             name=self.OP_NAME,
             metadata=Any(
@@ -543,202 +508,310 @@ def test_create_w_clusters(self):
                 value=metadata.SerializeToString(),
             )
         )
+        response = operation.from_gapic(
+            response_pb,
+            mock.Mock(),
+            instance_pb2.Instance,
+            metadata_type=messages_v2_pb2.UpdateInstanceMetadata,
+        )
+        instance_path_template = 'projects/{project}/instances/{instance}'
+        instance_api = mock.create_autospec(
+            bigtable_instance_admin_client.BigtableInstanceAdminClient)
+        instance_api.partial_update_instance.return_value = response
+        instance_api.instance_path = instance_path_template.format
+        return instance_api, response
 
-        # Patch the stub used by the API method.
-        channel = ChannelStub(responses=[response_pb])
-        instance_api = (
-            bigtable_instance_admin_client.BigtableInstanceAdminClient(
-                channel=channel))
-        client._instance_admin_client = instance_api
+    def test_update(self):
+        from google.cloud.bigtable import enums
+        from google.protobuf import field_mask_pb2
+        from google.cloud.bigtable_admin_v2.types import instance_pb2
 
-        # Perform the method and check the result.
-        cluster_id_1 = 'cluster-1'
-        cluster_id_2 = 'cluster-2'
-        location_id_1 = 'location-id-1'
-        location_id_2 = 'location-id-2'
-        serve_nodes_1 = 3
-        serve_nodes_2 = 5
-        clusters = [
-            Cluster(cluster_id_1, instance,
-                    location_id=location_id_1,
-                    serve_nodes=serve_nodes_1),
-            Cluster(cluster_id_2, instance,
-                    location_id=location_id_2,
-                    serve_nodes=serve_nodes_2)]
-        result = instance.create(clusters=clusters)
-        actual_request = channel.requests[0][1]
+        credentials = _make_credentials()
+        client = self._make_client(
+            project=self.PROJECT, credentials=credentials, admin=True)
+        instance = self._make_one(
+            self.INSTANCE_ID,
+            client,
+            display_name=self.DISPLAY_NAME,
+            instance_type=enums.Instance.Type.DEVELOPMENT,
+            labels=self.LABELS,
+        )
+        instance_api, response = self._instance_api_response_for_update()
+        client._instance_admin_client = instance_api
 
-        cluster_1_pb = self._create_cluster_pb(
-            instance_api, cluster_id_1, location_id_1, serve_nodes_1,
-            enums.StorageType.UNSPECIFIED)
+        result = instance.update()
 
-        cluster_2_pb = self._create_cluster_pb(
-            instance_api, cluster_id_2, location_id_2, serve_nodes_2,
-            enums.StorageType.UNSPECIFIED)
+        instance_pb = instance_pb2.Instance(
+            name=instance.name,
+            display_name=instance.display_name,
+            type=instance.type_,
+            labels=instance.labels,
+        )
+        update_mask_pb = field_mask_pb2.FieldMask(
+            paths=['display_name', 'type', 'labels'])
 
-        expected_request = self._create_instance_request(
-            {cluster_id_1: cluster_1_pb,
-             cluster_id_2: cluster_2_pb}
+        instance_api.partial_update_instance.assert_called_once_with(
+            instance=instance_pb,
+            update_mask=update_mask_pb,
         )
-        self.assertEqual(expected_request, actual_request)
-        self.assertIsInstance(result, operation.Operation)
-        self.assertEqual(result.operation.name, self.OP_NAME)
-        self.assertIsInstance(result.metadata,
-                              messages_v2_pb2.CreateInstanceMetadata)
-
-    def _create_cluster_pb(self, instance_api, cluster_id, location_id,
-                           serve_nodes, storage_type):
-        from google.cloud.bigtable_admin_v2.types import instance_pb2
 
-        location = instance_api.location_path(
-            self.PROJECT, location_id)
-        return instance_pb2.Cluster(
-            location=location,
-            serve_nodes=serve_nodes,
-            default_storage_type=storage_type)
+        self.assertIs(result, response)
 
-    def _create_instance_request(self, clusters):
-        from google.cloud.bigtable_admin_v2.proto import (
-            bigtable_instance_admin_pb2 as messages_v2_pb2)
+    def test_update_empty(self):
+        from google.protobuf import field_mask_pb2
         from google.cloud.bigtable_admin_v2.types import instance_pb2
-        from google.cloud.bigtable import enums
 
-        instance = instance_pb2.Instance(display_name=self.DISPLAY_NAME,
-                                         type=enums.Instance.Type.PRODUCTION,
-                                         labels=self.LABELS)
+        credentials = _make_credentials()
+        client = self._make_client(
+            project=self.PROJECT, credentials=credentials, admin=True)
+        instance = self._make_one(None, client)
+        instance_api, response = self._instance_api_response_for_update()
+        client._instance_admin_client = instance_api
 
-        return messages_v2_pb2.CreateInstanceRequest(
-            parent='projects/{}'.format(self.PROJECT),
-            instance_id=self.INSTANCE_ID,
-            instance=instance,
-            clusters=clusters
+        result = instance.update()
+
+        instance_pb = instance_pb2.Instance(
+            name=instance.name,
+            display_name=instance.display_name,
+            type=instance.type_,
+            labels=instance.labels,
         )
+        update_mask_pb = field_mask_pb2.FieldMask()
 
-    def test_update(self):
-        import datetime
-        from google.api_core import operation
-        from google.longrunning import operations_pb2
-        from google.protobuf.any_pb2 import Any
-        from google.cloud.bigtable_admin_v2.proto import (
-            bigtable_instance_admin_pb2 as messages_v2_pb2)
-        from google.cloud._helpers import _datetime_to_pb_timestamp
-        from google.cloud.bigtable import enums
+        instance_api.partial_update_instance.assert_called_once_with(
+            instance=instance_pb,
+            update_mask=update_mask_pb,
+        )
+
+        self.assertIs(result, response)
+
+    def test_delete(self):
         from google.cloud.bigtable_admin_v2.gapic import (
             bigtable_instance_admin_client)
-        from google.protobuf import field_mask_pb2
-        from google.cloud.bigtable_admin_v2.types import instance_pb2
-        from google.cloud.bigtable_admin_v2.proto import (
-            bigtable_instance_admin_pb2 as instance_v2_pb2)
 
-        NOW = datetime.datetime.utcnow()
-        NOW_PB = _datetime_to_pb_timestamp(NOW)
         credentials = _make_credentials()
         client = self._make_client(project=self.PROJECT,
                                    credentials=credentials, admin=True)
-        instance = self._make_one(
-            self.INSTANCE_ID, client, display_name=self.DISPLAY_NAME,
-            instance_type=enums.Instance.Type.DEVELOPMENT, labels=self.LABELS)
+        instance = self._make_one(self.INSTANCE_ID, client)
+        instance_api = mock.create_autospec(
+            bigtable_instance_admin_client.BigtableInstanceAdminClient)
+        instance_api.delete_instance.return_value = None
+        client._instance_admin_client = instance_api
 
-        expected_request_instance = instance_pb2.Instance(
-            name=instance.name, display_name=instance.display_name,
-            type=instance.type_, labels=instance.labels)
-        expected_request_update_mask = field_mask_pb2.FieldMask(
-            paths=['display_name', 'type', 'labels'])
-        expected_request = instance_v2_pb2.PartialUpdateInstanceRequest(
-            instance=expected_request_instance,
-            update_mask=expected_request_update_mask)
+        result = instance.delete()
 
-        metadata = messages_v2_pb2.UpdateInstanceMetadata(
-            request_time=NOW_PB)
-        type_url = 'type.googleapis.com/{}'.format(
-            messages_v2_pb2.UpdateInstanceMetadata.DESCRIPTOR.full_name)
-        response_pb = operations_pb2.Operation(
-            name=self.OP_NAME,
-            metadata=Any(
-                type_url=type_url,
-                value=metadata.SerializeToString(),
-            )
-        )
+        instance_api.delete_instance.assert_called_once_with(
+            instance.name)
 
-        channel = ChannelStub(responses=[response_pb])
-        instance_api = (
-            bigtable_instance_admin_client.BigtableInstanceAdminClient(
-                channel=channel))
+        self.assertIsNone(result)
 
-        # Mock api calls
+    def test_get_iam_policy(self):
+        from google.cloud.bigtable_admin_v2.gapic import (
+            bigtable_instance_admin_client)
+        from google.iam.v1 import policy_pb2
+        from google.cloud.bigtable.policy import BIGTABLE_ADMIN_ROLE
+
+        credentials = _make_credentials()
+        client = self._make_client(project=self.PROJECT,
+                                   credentials=credentials, admin=True)
+        instance = self._make_one(self.INSTANCE_ID, client)
+
+        version = 1
+        etag = b'etag_v1'
+        members = [
+            'serviceAccount:service_acc1@test.com',
+            'user:user1@test.com',
+        ]
+        bindings = [{'role': BIGTABLE_ADMIN_ROLE, 'members': members}]
+        iam_policy = policy_pb2.Policy(
+            version=version, etag=etag, bindings=bindings)
+
+        # Patch the stub used by the API method.
+        instance_api = mock.create_autospec(
+            bigtable_instance_admin_client.BigtableInstanceAdminClient)
         client._instance_admin_client = instance_api
+        instance_api.get_iam_policy.return_value = iam_policy
 
         # Perform the method and check the result.
-        result = instance.update()
-        actual_request = channel.requests[0][1]
+        result = instance.get_iam_policy()
 
-        self.assertEqual(actual_request, expected_request)
-        self.assertIsInstance(result, operation.Operation)
-        self.assertEqual(result.operation.name, self.OP_NAME)
-        self.assertIsInstance(result.metadata,
-                              messages_v2_pb2.UpdateInstanceMetadata)
+        instance_api.get_iam_policy.assert_called_once_with(
+            resource=instance.name)
+        self.assertEqual(result.version, version)
+        self.assertEqual(result.etag, etag)
+        admins = result.bigtable_admins
+        self.assertEqual(len(admins), len(members))
+        for found, expected in zip(sorted(admins), sorted(members)):
+            self.assertEqual(found, expected)
 
-    def test_update_empty(self):
-        from google.api_core import operation
+    def test_set_iam_policy(self):
         from google.cloud.bigtable_admin_v2.gapic import (
             bigtable_instance_admin_client)
-        from google.longrunning import operations_pb2
-        from google.protobuf import field_mask_pb2
-        from google.cloud.bigtable_admin_v2.types import instance_pb2
-        from google.cloud.bigtable_admin_v2.proto import (
-            bigtable_instance_admin_pb2 as instance_v2_pb2)
+        from google.iam.v1 import policy_pb2
+        from google.cloud.bigtable.policy import Policy
+        from google.cloud.bigtable.policy import BIGTABLE_ADMIN_ROLE
 
         credentials = _make_credentials()
         client = self._make_client(project=self.PROJECT,
                                    credentials=credentials, admin=True)
-        instance = self._make_one(None, client)
-
-        expected_request_instance = instance_pb2.Instance(
-            name=instance.name, display_name=instance.display_name,
-            type=instance.type_, labels=instance.labels)
-        expected_request_update_mask = field_mask_pb2.FieldMask()
-        expected_request = instance_v2_pb2.PartialUpdateInstanceRequest(
-            instance=expected_request_instance,
-            update_mask=expected_request_update_mask)
-
-        response_pb = operations_pb2.Operation(name=self.OP_NAME)
+        instance = self._make_one(self.INSTANCE_ID, client)
 
-        channel = ChannelStub(responses=[response_pb])
-        instance_api = (
-            bigtable_instance_admin_client.BigtableInstanceAdminClient(
-                channel=channel))
+        version = 1
+        etag = b'etag_v1'
+        members = [
+            'serviceAccount:service_acc1@test.com',
+            'user:user1@test.com',
+        ]
+        bindings = [{'role': BIGTABLE_ADMIN_ROLE, 'members': members}]
+        iam_policy_pb = policy_pb2.Policy(
+            version=version, etag=etag, bindings=bindings)
 
-        # Mock api calls
+        # Patch the stub used by the API method.
+        instance_api = mock.create_autospec(
+            bigtable_instance_admin_client.BigtableInstanceAdminClient)
+        instance_api.set_iam_policy.return_value = iam_policy_pb
         client._instance_admin_client = instance_api
 
         # Perform the method and check the result.
-        result = instance.update()
-        actual_request = channel.requests[0][1]
+        iam_policy = Policy(etag=etag, version=version)
+        iam_policy[BIGTABLE_ADMIN_ROLE] = [
+            Policy.user("user1@test.com"),
+            Policy.service_account("service_acc1@test.com"),
+        ]
 
-        self.assertIsInstance(result, operation.Operation)
-        self.assertEqual(actual_request, expected_request)
+        result = instance.set_iam_policy(iam_policy)
 
-    def test_delete(self):
+        instance_api.set_iam_policy.assert_called_once_with(
+            resource=instance.name,
+            policy={
+                'version': version,
+                'etag': etag,
+                'bindings': bindings,
+            },
+        )
+        self.assertEqual(result.version, version)
+        self.assertEqual(result.etag, etag)
+        admins = result.bigtable_admins
+        self.assertEqual(len(admins), len(members))
+        for found, expected in zip(sorted(admins), sorted(members)):
+            self.assertEqual(found, expected)
+
+    def test_test_iam_permissions(self):
         from google.cloud.bigtable_admin_v2.gapic import (
             bigtable_instance_admin_client)
+        from google.iam.v1 import iam_policy_pb2
 
-        api = bigtable_instance_admin_client.BigtableInstanceAdminClient(
-            mock.Mock())
         credentials = _make_credentials()
         client = self._make_client(project=self.PROJECT,
                                    credentials=credentials, admin=True)
         instance = self._make_one(self.INSTANCE_ID, client)
 
-        # Mock api calls
-        client._instance_admin_client = api
+        permissions = ["bigtable.tables.create", "bigtable.clusters.create"]
 
-        # Create expected_result.
-        expected_result = None  # delete() has no return value.
+        response = iam_policy_pb2.TestIamPermissionsResponse(
+            permissions=permissions)
+
+        instance_api = mock.create_autospec(
+            bigtable_instance_admin_client.BigtableInstanceAdminClient)
+        instance_api.test_iam_permissions.return_value = response
+        client._instance_admin_client = instance_api
+
+        result = instance.test_iam_permissions(permissions)
+
+        self.assertEqual(result, permissions)
+        instance_api.test_iam_permissions.assert_called_once_with(
+            resource=instance.name, permissions=permissions)
+
+    def test_cluster_factory(self):
+        from google.cloud.bigtable import enums
+
+        CLUSTER_ID = '{}-cluster'.format(self.INSTANCE_ID)
+        LOCATION_ID = 'us-central1-c'
+        SERVE_NODES = 3
+        STORAGE_TYPE = enums.StorageType.HDD
+
+        instance = self._make_one(self.INSTANCE_ID, None)
+
+        cluster = instance.cluster(CLUSTER_ID, location_id=LOCATION_ID,
+                                   serve_nodes=SERVE_NODES,
+                                   default_storage_type=STORAGE_TYPE)
+        self.assertIsInstance(cluster, Cluster)
+        self.assertEqual(cluster.cluster_id, CLUSTER_ID)
+        self.assertEqual(cluster.location_id, LOCATION_ID)
+        self.assertIsNone(cluster._state)
+        self.assertEqual(cluster.serve_nodes, SERVE_NODES)
+        self.assertEqual(cluster.default_storage_type, STORAGE_TYPE)
+
+    def test_list_clusters(self):
+        from google.cloud.bigtable_admin_v2.gapic import (
+            bigtable_instance_admin_client)
+        from google.cloud.bigtable_admin_v2.proto import (
+            bigtable_instance_admin_pb2 as messages_v2_pb2)
+        from google.cloud.bigtable_admin_v2.proto import (
+            instance_pb2 as data_v2_pb2)
+        from google.cloud.bigtable.instance import Instance
+        from google.cloud.bigtable.instance import Cluster
+
+        credentials = _make_credentials()
+        client = self._make_client(project=self.PROJECT,
+                                   credentials=credentials, admin=True)
+        instance = Instance(self.INSTANCE_ID, client)
+
+        failed_location = 'FAILED'
+        cluster_id1 = 'cluster-id1'
+        cluster_id2 = 'cluster-id2'
+        cluster_path_template = 'projects/{}/instances/{}/clusters/{}'
+        cluster_name1 = cluster_path_template.format(
+                         self.PROJECT, self.INSTANCE_ID, cluster_id1)
+        cluster_name2 = cluster_path_template.format(
+                         self.PROJECT, self.INSTANCE_ID, cluster_id2)
+
+        # Create response_pb
+        response_pb = messages_v2_pb2.ListClustersResponse(
+            failed_locations=[
+                failed_location
+            ],
+            clusters=[
+                data_v2_pb2.Cluster(
+                    name=cluster_name1,
+                ),
+                data_v2_pb2.Cluster(
+                    name=cluster_name2,
+                ),
+            ],
+        )
+
+        # Patch the stub used by the API method.
+        instance_api = mock.create_autospec(
+            bigtable_instance_admin_client.BigtableInstanceAdminClient)
+        instance_api.list_clusters.side_effect = [response_pb]
+        instance_api.cluster_path = cluster_path_template.format
+        client._instance_admin_client = instance_api
 
         # Perform the method and check the result.
-        result = instance.delete()
+        clusters, failed_locations = instance.list_clusters()
 
-        self.assertEqual(result, expected_result)
+        cluster_1, cluster_2 = clusters
+
+        self.assertIsInstance(cluster_1, Cluster)
+        self.assertEqual(cluster_1.name, cluster_name1)
+
+        self.assertIsInstance(cluster_2, Cluster)
+        self.assertEqual(cluster_2.name, cluster_name2)
+
+        self.assertEqual(failed_locations, [failed_location])
+
+    def test_table_factory(self):
+        from google.cloud.bigtable.table import Table
+
+        app_profile_id = 'appProfileId1262094415'
+        instance = self._make_one(self.INSTANCE_ID, None)
+
+        table = instance.table(self.TABLE_ID, app_profile_id=app_profile_id)
+        self.assertIsInstance(table, Table)
+        self.assertEqual(table.table_id, self.TABLE_ID)
+        self.assertEqual(table._instance, instance)
+        self.assertEqual(table._app_profile_id, app_profile_id)
 
     def _list_tables_helper(self, table_name=None):
         from google.cloud.bigtable_admin_v2.proto import (
@@ -838,17 +911,24 @@ def test_app_profile_factory(self):
         self.assertEqual(app_profile2.allow_transactional_writes, ALLOW_WRITES)
 
     def test_list_app_profiles(self):
+        from google.api_core.page_iterator import Iterator
+        from google.api_core.page_iterator import Page
         from google.cloud.bigtable_admin_v2.gapic import (
             bigtable_instance_admin_client)
-        from google.cloud.bigtable_admin_v2.proto import (
-            bigtable_instance_admin_pb2 as messages_v2_pb2)
         from google.cloud.bigtable_admin_v2.proto import (
             instance_pb2 as data_v2_pb2)
         from google.cloud.bigtable.app_profile import AppProfile
 
-        instance_api = (
-            bigtable_instance_admin_client.BigtableInstanceAdminClient(
-                mock.Mock()))
+        class _Iterator(Iterator):
+
+            def __init__(self, pages):
+                super(_Iterator, self).__init__(client=None)
+                self._pages = pages
+
+            def _next_page(self):
+                if self._pages:
+                    page, self._pages = self._pages[0], self._pages[1:]
+                    return Page(self, page, self.item_to_value)
 
         credentials = _make_credentials()
         client = self._make_client(project=self.PROJECT,
@@ -856,35 +936,33 @@ def test_list_app_profiles(self):
         instance = self._make_one(self.INSTANCE_ID, client)
 
         # Setup Expected Response
-        next_page_token = ''
+        app_profile_path_template = 'projects/{}/instances/{}/appProfiles/{}'
         app_profile_id1 = 'app-profile-id1'
         app_profile_id2 = 'app-profile-id2'
-        app_profile_name1 = (client.instance_admin_client.app_profile_path(
-            self.PROJECT, self.INSTANCE_ID, app_profile_id1))
-        app_profile_name2 = (client.instance_admin_client.app_profile_path(
-            self.PROJECT, self.INSTANCE_ID, app_profile_id2))
+        app_profile_name1 = app_profile_path_template.format(
+            self.PROJECT, self.INSTANCE_ID, app_profile_id1)
+        app_profile_name2 = app_profile_path_template.format(
+            self.PROJECT, self.INSTANCE_ID, app_profile_id2)
         routing_policy = data_v2_pb2.AppProfile.MultiClusterRoutingUseAny()
 
-        expected_response = messages_v2_pb2.ListAppProfilesResponse(
-            next_page_token=next_page_token,
-            app_profiles=[
-                data_v2_pb2.AppProfile(
-                    name=app_profile_name1,
-                    multi_cluster_routing_use_any=routing_policy,
-                ),
-                data_v2_pb2.AppProfile(
-                    name=app_profile_name2,
-                    multi_cluster_routing_use_any=routing_policy,
-                )
-            ],
-        )
+        app_profiles = [
+            data_v2_pb2.AppProfile(
+                name=app_profile_name1,
+                multi_cluster_routing_use_any=routing_policy,
+            ),
+            data_v2_pb2.AppProfile(
+                name=app_profile_name2,
+                multi_cluster_routing_use_any=routing_policy,
+            )
+        ]
+        iterator = _Iterator(pages=[app_profiles])
 
         # Patch the stub used by the API method.
+        instance_api = mock.create_autospec(
+            bigtable_instance_admin_client.BigtableInstanceAdminClient)
         client._instance_admin_client = instance_api
-        bigtable_instance_stub = (
-            client._instance_admin_client.transport)
-        bigtable_instance_stub.list_app_profiles.side_effect = [
-            expected_response]
+        instance_api.app_profile_path = app_profile_path_template.format
+        instance_api.list_app_profiles.return_value = iterator
 
         # Perform the method and check the result.
         app_profiles = instance.list_app_profiles()
@@ -896,136 +974,3 @@ def test_list_app_profiles(self):
 
         self.assertIsInstance(app_profile_2, AppProfile)
         self.assertEqual(app_profile_2.name, app_profile_name2)
-
-    def test_get_iam_policy(self):
-        from google.cloud.bigtable_admin_v2.gapic import (
-            bigtable_instance_admin_client)
-        from google.iam.v1 import iam_policy_pb2
-        from google.iam.v1 import policy_pb2
-        from google.cloud.bigtable.policy import Policy
-        from google.cloud.bigtable.policy import BIGTABLE_ADMIN_ROLE
-
-        credentials = _make_credentials()
-        client = self._make_client(project=self.PROJECT,
-                                   credentials=credentials, admin=True)
-        instance = self._make_one(self.INSTANCE_ID, client)
-
-        version = 1
-        etag = b'etag_v1'
-        bindings = [{'role': BIGTABLE_ADMIN_ROLE,
-                     'members': ['serviceAccount:service_acc1@test.com',
-                                 'user:user1@test.com']}]
-
-        expected_request_policy = policy_pb2.Policy(version=version,
-                                                    etag=etag,
-                                                    bindings=bindings)
-
-        expected_request = iam_policy_pb2.GetIamPolicyRequest(
-            resource=instance.name
-        )
-
-        # Patch the stub used by the API method.
-        channel = ChannelStub(responses=[expected_request_policy])
-        instance_api = (
-            bigtable_instance_admin_client.BigtableInstanceAdminClient(
-                channel=channel))
-        client._instance_admin_client = instance_api
-        # Perform the method and check the result.
-        policy_request = Policy(etag=etag, version=version)
-        policy_request[BIGTABLE_ADMIN_ROLE] = [Policy.user("user1@test.com"),
-                                               Policy.service_account(
-                                                   "service_acc1@test.com")]
-
-        result = instance.get_iam_policy()
-        actual_request = channel.requests[0][1]
-
-        self.assertEqual(actual_request, expected_request)
-        self.assertEqual(result.bigtable_admins,
-                         policy_request.bigtable_admins)
-
-    def test_set_iam_policy(self):
-        from google.cloud.bigtable_admin_v2.gapic import (
-            bigtable_instance_admin_client)
-        from google.iam.v1 import iam_policy_pb2
-        from google.iam.v1 import policy_pb2
-        from google.cloud.bigtable.policy import Policy
-        from google.cloud.bigtable.policy import BIGTABLE_ADMIN_ROLE
-
-        credentials = _make_credentials()
-        client = self._make_client(project=self.PROJECT,
-                                   credentials=credentials, admin=True)
-        instance = self._make_one(self.INSTANCE_ID, client)
-
-        version = 1
-        etag = b'etag_v1'
-        bindings = [{'role': BIGTABLE_ADMIN_ROLE,
-                     'members': ['serviceAccount:service_acc1@test.com',
-                                 'user:user1@test.com']}]
-
-        expected_request_policy = policy_pb2.Policy(version=version,
-                                                    etag=etag,
-                                                    bindings=bindings)
-
-        expected_request = iam_policy_pb2.SetIamPolicyRequest(
-            resource=instance.name,
-            policy=expected_request_policy
-        )
-
-        # Patch the stub used by the API method.
-        channel = ChannelStub(responses=[expected_request_policy])
-        instance_api = (
-            bigtable_instance_admin_client.BigtableInstanceAdminClient(
-                channel=channel))
-        client._instance_admin_client = instance_api
-        # Perform the method and check the result.
-        policy_request = Policy(etag=etag, version=version)
-        policy_request[BIGTABLE_ADMIN_ROLE] = [Policy.user("user1@test.com"),
-                                               Policy.service_account(
-                                                   "service_acc1@test.com")]
-
-        result = instance.set_iam_policy(policy_request)
-        actual_request = channel.requests[0][1]
-
-        self.assertEqual(actual_request, expected_request)
-        self.assertEqual(result.bigtable_admins,
-                         policy_request.bigtable_admins)
-
-    def test_test_iam_permissions(self):
-        from google.cloud.bigtable_admin_v2.gapic import (
-            bigtable_instance_admin_client)
-        from google.iam.v1 import iam_policy_pb2
-
-        credentials = _make_credentials()
-        client = self._make_client(project=self.PROJECT,
-                                   credentials=credentials, admin=True)
-        instance = self._make_one(self.INSTANCE_ID, client)
-
-        permissions = ["bigtable.tables.create", "bigtable.clusters.create"]
-
-        expected_request = iam_policy_pb2.TestIamPermissionsRequest(
-            resource=instance.name,
-            permissions=permissions)
-
-        # Patch the stub used by the API method.
-        channel = ChannelStub(responses=[expected_request])
-        instance_api = (
-            bigtable_instance_admin_client.BigtableInstanceAdminClient(
-                channel=channel))
-        client._instance_admin_client = instance_api
-
-        result = instance.test_iam_permissions(permissions)
-        actual_request = channel.requests[0][1]
-        self.assertEqual(actual_request, expected_request)
-        self.assertEqual(result, permissions)
-
-
-class _Client(object):
-
-    def __init__(self, project):
-        self.project = project
-        self.project_name = 'projects/' + self.project
-        self._operations_stub = mock.sentinel.operations_stub
-
-    def __eq__(self, other):
-        return (other.project == self.project and
-                other.project_name == self.project_name)