Repository: googleapis/python-bigquery

Issue:
Extract common features of `Table*` classes to a new base class
In #854 `Table*` instances were made equal to each other if they referenced the same table.

During the implementation it turned out that `Table`, `TableReference`, and `TableListItem` have several nearly identical parts that could be placed in a new base class. For example:
 - The `project`, `table_id`, and `dataset_id` properties.
 - The `__hash__()` and `__eq__()` special methods.

Extract these and any other features into a new common base class.



Discussion:

Resolving patch:
diff --git a/google/cloud/bigquery/_helpers.py b/google/cloud/bigquery/_helpers.py
--- a/google/cloud/bigquery/_helpers.py
+++ b/google/cloud/bigquery/_helpers.py
@@ -672,8 +672,9 @@ def _get_sub_prop(container, keys, default=None):
         container (Dict):
             A dictionary which may contain other dictionaries as values.
         keys (Iterable):
-            A sequence of keys to attempt to get the value for. Each item in
-            the sequence represents a deeper nesting. The first key is for
+            A sequence of keys to attempt to get the value for. If ``keys`` is a
+            string, it is treated as sequence containing a single string key. Each item
+            in the sequence represents a deeper nesting. The first key is for
             the top level. If there is a dictionary there, the second key
             attempts to get the value within that, and so on.
         default (Optional[object]):
@@ -700,6 +701,9 @@ def _get_sub_prop(container, keys, default=None):
     Returns:
         object: The value if present or the default.
     """
+    if isinstance(keys, str):
+        keys = [keys]
+
     sub_val = container
     for key in keys:
         if key not in sub_val:
@@ -715,8 +719,9 @@ def _set_sub_prop(container, keys, value):
         container (Dict):
             A dictionary which may contain other dictionaries as values.
         keys (Iterable):
-            A sequence of keys to attempt to set the value for. Each item in
-            the sequence represents a deeper nesting. The first key is for
+            A sequence of keys to attempt to set the value for. If ``keys`` is a
+            string, it is treated as sequence containing a single string key. Each item
+            in the sequence represents a deeper nesting. The first key is for
             the top level. If there is a dictionary there, the second key
             attempts to get the value within that, and so on.
         value (object): Value to set within the container.
@@ -743,6 +748,9 @@ def _set_sub_prop(container, keys, value):
         >>> container
         {'key': {'subkey': 'new'}}
     """
+    if isinstance(keys, str):
+        keys = [keys]
+
     sub_val = container
     for key in keys[:-1]:
         if key not in sub_val:
diff --git a/google/cloud/bigquery/table.py b/google/cloud/bigquery/table.py
--- a/google/cloud/bigquery/table.py
+++ b/google/cloud/bigquery/table.py
@@ -69,6 +69,7 @@
     import geopandas
     import pyarrow
     from google.cloud import bigquery_storage
+    from google.cloud.bigquery.dataset import DatasetReference
 
 
 _NO_PANDAS_ERROR = (
@@ -126,45 +127,93 @@ def _view_use_legacy_sql_getter(table):
         return True
 
 
-class TableReference(object):
-    """TableReferences are pointers to tables.
+class _TableBase:
+    """Base class for Table-related classes with common functionality."""
 
-    See
-    https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#tablereference
-
-    Args:
-        dataset_ref (google.cloud.bigquery.dataset.DatasetReference):
-            A pointer to the dataset
-        table_id (str): The ID of the table
-    """
+    _PROPERTY_TO_API_FIELD = {
+        "dataset_id": ["tableReference", "datasetId"],
+        "project": ["tableReference", "projectId"],
+        "table_id": ["tableReference", "tableId"],
+    }
 
-    def __init__(self, dataset_ref, table_id):
-        self._project = dataset_ref.project
-        self._dataset_id = dataset_ref.dataset_id
-        self._table_id = table_id
+    def __init__(self):
+        self._properties = {}
 
     @property
-    def project(self):
-        """str: Project bound to the table"""
-        return self._project
+    def project(self) -> str:
+        """Project bound to the table."""
+        return _helpers._get_sub_prop(
+            self._properties, self._PROPERTY_TO_API_FIELD["project"]
+        )
 
     @property
-    def dataset_id(self):
-        """str: ID of dataset containing the table."""
-        return self._dataset_id
+    def dataset_id(self) -> str:
+        """ID of dataset containing the table."""
+        return _helpers._get_sub_prop(
+            self._properties, self._PROPERTY_TO_API_FIELD["dataset_id"]
+        )
 
     @property
-    def table_id(self):
-        """str: The table ID."""
-        return self._table_id
+    def table_id(self) -> str:
+        """The table ID."""
+        return _helpers._get_sub_prop(
+            self._properties, self._PROPERTY_TO_API_FIELD["table_id"]
+        )
 
     @property
-    def path(self):
-        """str: URL path for the table's APIs."""
-        return "/projects/%s/datasets/%s/tables/%s" % (
-            self._project,
-            self._dataset_id,
-            self._table_id,
+    def path(self) -> str:
+        """URL path for the table's APIs."""
+        return (
+            f"/projects/{self.project}/datasets/{self.dataset_id}"
+            f"/tables/{self.table_id}"
+        )
+
+    def __eq__(self, other):
+        if isinstance(other, _TableBase):
+            return (
+                self.project == other.project
+                and self.dataset_id == other.dataset_id
+                and self.table_id == other.table_id
+            )
+        else:
+            return NotImplemented
+
+    def __hash__(self):
+        return hash((self.project, self.dataset_id, self.table_id))
+
+
+class TableReference(_TableBase):
+    """TableReferences are pointers to tables.
+
+    See
+    https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#tablereference
+
+    Args:
+        dataset_ref: A pointer to the dataset
+        table_id: The ID of the table
+    """
+
+    _PROPERTY_TO_API_FIELD = {
+        "dataset_id": "datasetId",
+        "project": "projectId",
+        "table_id": "tableId",
+    }
+
+    def __init__(self, dataset_ref: "DatasetReference", table_id: str):
+        self._properties = {}
+
+        _helpers._set_sub_prop(
+            self._properties,
+            self._PROPERTY_TO_API_FIELD["project"],
+            dataset_ref.project,
+        )
+        _helpers._set_sub_prop(
+            self._properties,
+            self._PROPERTY_TO_API_FIELD["dataset_id"],
+            dataset_ref.dataset_id,
+        )
+        _helpers._set_sub_prop(
+            self._properties, self._PROPERTY_TO_API_FIELD["table_id"], table_id,
         )
 
     @classmethod
@@ -233,11 +282,7 @@ def to_api_repr(self) -> dict:
         Returns:
             Dict[str, object]: Table reference represented as an API resource
         """
-        return {
-            "projectId": self._project,
-            "datasetId": self._dataset_id,
-            "tableId": self._table_id,
-        }
+        return copy.deepcopy(self._properties)
 
     def to_bqstorage(self) -> str:
         """Construct a BigQuery Storage API representation of this table.
@@ -257,54 +302,25 @@ def to_bqstorage(self) -> str:
             str: A reference to this table in the BigQuery Storage API.
         """
 
-        table_id, _, _ = self._table_id.partition("@")
+        table_id, _, _ = self.table_id.partition("@")
         table_id, _, _ = table_id.partition("$")
 
-        table_ref = "projects/{}/datasets/{}/tables/{}".format(
-            self._project, self._dataset_id, table_id,
+        table_ref = (
+            f"projects/{self.project}/datasets/{self.dataset_id}/tables/{table_id}"
         )
-
         return table_ref
 
-    def _key(self):
-        """A tuple key that uniquely describes this field.
-
-        Used to compute this instance's hashcode and evaluate equality.
-
-        Returns:
-            Tuple[str]: The contents of this :class:`DatasetReference`.
-        """
-        return (self._project, self._dataset_id, self._table_id)
-
-    def __eq__(self, other):
-        if isinstance(other, (Table, TableListItem)):
-            return (
-                self.project == other.project
-                and self.dataset_id == other.dataset_id
-                and self.table_id == other.table_id
-            )
-        elif isinstance(other, TableReference):
-            return self._key() == other._key()
-        else:
-            return NotImplemented
-
-    def __ne__(self, other):
-        return not self == other
-
-    def __hash__(self):
-        return hash(self._key())
-
     def __str__(self):
         return f"{self.project}.{self.dataset_id}.{self.table_id}"
 
     def __repr__(self):
         from google.cloud.bigquery.dataset import DatasetReference
 
-        dataset_ref = DatasetReference(self._project, self._dataset_id)
-        return "TableReference({}, '{}')".format(repr(dataset_ref), self._table_id)
+        dataset_ref = DatasetReference(self.project, self.dataset_id)
+        return f"TableReference({dataset_ref!r}, '{self.table_id}')"
 
 
-class Table(object):
+class Table(_TableBase):
     """Tables represent a set of rows whose values correspond to a schema.
 
     See
@@ -325,9 +341,9 @@ class Table(object):
     """
 
     _PROPERTY_TO_API_FIELD = {
+        **_TableBase._PROPERTY_TO_API_FIELD,
         "clustering_fields": "clustering",
         "created": "creationTime",
-        "dataset_id": ["tableReference", "datasetId"],
         "description": "description",
         "encryption_configuration": "encryptionConfiguration",
         "etag": "etag",
@@ -346,14 +362,12 @@ class Table(object):
         "num_rows": "numRows",
         "partition_expiration": "timePartitioning",
         "partitioning_type": "timePartitioning",
-        "project": ["tableReference", "projectId"],
         "range_partitioning": "rangePartitioning",
         "time_partitioning": "timePartitioning",
         "schema": "schema",
         "snapshot_definition": "snapshotDefinition",
         "streaming_buffer": "streamingBuffer",
         "self_link": "selfLink",
-        "table_id": ["tableReference", "tableId"],
         "time_partitioning": "timePartitioning",
         "type": "type",
         "view_use_legacy_sql": "view",
@@ -368,38 +382,8 @@ def __init__(self, table_ref, schema=None):
         if schema is not None:
             self.schema = schema
 
-    @property
-    def project(self):
-        """str: Project bound to the table."""
-        return _helpers._get_sub_prop(
-            self._properties, self._PROPERTY_TO_API_FIELD["project"]
-        )
-
-    @property
-    def dataset_id(self):
-        """str: ID of dataset containing the table."""
-        return _helpers._get_sub_prop(
-            self._properties, self._PROPERTY_TO_API_FIELD["dataset_id"]
-        )
-
-    @property
-    def table_id(self):
-        """str: ID of the table."""
-        return _helpers._get_sub_prop(
-            self._properties, self._PROPERTY_TO_API_FIELD["table_id"]
-        )
-
     reference = property(_reference_getter)
 
-    @property
-    def path(self):
-        """str: URL path for the table's APIs."""
-        return "/projects/%s/datasets/%s/tables/%s" % (
-            self.project,
-            self.dataset_id,
-            self.table_id,
-        )
-
     @property
     def require_partition_filter(self):
         """bool: If set to true, queries over the partitioned table require a
@@ -1040,29 +1024,11 @@ def _build_resource(self, filter_fields):
         """Generate a resource for ``update``."""
         return _helpers._build_resource_from_properties(self, filter_fields)
 
-    def __eq__(self, other):
-        if isinstance(other, Table):
-            return (
-                self._properties["tableReference"]
-                == other._properties["tableReference"]
-            )
-        elif isinstance(other, (TableReference, TableListItem)):
-            return (
-                self.project == other.project
-                and self.dataset_id == other.dataset_id
-                and self.table_id == other.table_id
-            )
-        else:
-            return NotImplemented
-
-    def __hash__(self):
-        return hash((self.project, self.dataset_id, self.table_id))
-
     def __repr__(self):
         return "Table({})".format(repr(self.reference))
 
 
-class TableListItem(object):
+class TableListItem(_TableBase):
     """A read-only table resource from a list operation.
 
     For performance reasons, the BigQuery API only includes some of the table
@@ -1126,21 +1092,6 @@ def expires(self):
                 1000.0 * float(expiration_time)
             )
 
-    @property
-    def project(self):
-        """str: Project bound to the table."""
-        return self._properties["tableReference"]["projectId"]
-
-    @property
-    def dataset_id(self):
-        """str: ID of dataset containing the table."""
-        return self._properties["tableReference"]["datasetId"]
-
-    @property
-    def table_id(self):
-        """str: ID of the table."""
-        return self._properties["tableReference"]["tableId"]
-
     reference = property(_reference_getter)
 
     @property
@@ -1276,19 +1227,6 @@ def to_api_repr(self) -> dict:
         """
         return copy.deepcopy(self._properties)
 
-    def __eq__(self, other):
-        if isinstance(other, (Table, TableReference, TableListItem)):
-            return (
-                self.project == other.project
-                and self.dataset_id == other.dataset_id
-                and self.table_id == other.table_id
-            )
-        else:
-            return NotImplemented
-
-    def __hash__(self):
-        return hash((self.project, self.dataset_id, self.table_id))
-
 
 def _row_from_mapping(mapping, schema):
     """Convert a mapping to a row tuple using the schema.


Test patch:
diff --git a/tests/unit/test__helpers.py b/tests/unit/test__helpers.py
--- a/tests/unit/test__helpers.py
+++ b/tests/unit/test__helpers.py
@@ -1214,9 +1214,13 @@ def test_w_empty_container_default_default(self):
     def test_w_missing_key_explicit_default(self):
         self.assertEqual(self._call_fut({"key2": 2}, ["key1"], default=1), 1)
 
-    def test_w_matching_single_key(self):
+    def test_w_matching_single_key_in_sequence(self):
         self.assertEqual(self._call_fut({"key1": 1}, ["key1"]), 1)
 
+    def test_w_matching_single_string_key(self):
+        data = {"k": {"e": {"y": "foo"}}, "key": "bar"}
+        self.assertEqual(self._call_fut(data, "key"), "bar")
+
     def test_w_matching_first_key_missing_second_key(self):
         self.assertIsNone(self._call_fut({"key1": {"key3": 3}}, ["key1", "key2"]))
 
@@ -1230,11 +1234,16 @@ def _call_fut(self, container, keys, value):
 
         return _set_sub_prop(container, keys, value)
 
-    def test_w_empty_container_single_key(self):
+    def test_w_empty_container_single_key_in_sequence(self):
         container = {}
         self._call_fut(container, ["key1"], "value")
         self.assertEqual(container, {"key1": "value"})
 
+    def test_w_empty_container_single_string_key(self):
+        container = {}
+        self._call_fut(container, "key", "value")
+        self.assertEqual(container, {"key": "value"})
+
     def test_w_empty_container_nested_keys(self):
         container = {}
         self._call_fut(container, ["key1", "key2", "key3"], "value")
diff --git a/tests/unit/test_table.py b/tests/unit/test_table.py
--- a/tests/unit/test_table.py
+++ b/tests/unit/test_table.py
@@ -102,6 +102,189 @@ def test_ctor_with_key(self):
         self.assertEqual(encryption_config.kms_key_name, self.KMS_KEY_NAME)
 
 
+class TestTableBase:
+    @staticmethod
+    def _get_target_class():
+        from google.cloud.bigquery.table import _TableBase
+
+        return _TableBase
+
+    def _make_one(self, *args, **kw):
+        return self._get_target_class()(*args, **kw)
+
+    def test_ctor_defaults(self):
+        instance = self._make_one()
+        assert instance._properties == {}
+
+    def test_project(self):
+        instance = self._make_one()
+        instance._properties = {"tableReference": {"projectId": "p_1"}}
+        assert instance.project == "p_1"
+
+    def test_dataset_id(self):
+        instance = self._make_one()
+        instance._properties = {"tableReference": {"datasetId": "ds_1"}}
+        assert instance.dataset_id == "ds_1"
+
+    def test_table_id(self):
+        instance = self._make_one()
+        instance._properties = {"tableReference": {"tableId": "tbl_1"}}
+        assert instance.table_id == "tbl_1"
+
+    def test_path(self):
+        instance = self._make_one()
+        instance._properties = {
+            "tableReference": {
+                "projectId": "p_1",
+                "datasetId": "ds_1",
+                "tableId": "tbl_1",
+            }
+        }
+        assert instance.path == "/projects/p_1/datasets/ds_1/tables/tbl_1"
+
+    def test___eq___wrong_type(self):
+        instance = self._make_one()
+        instance._properties = {
+            "tableReference": {
+                "projectId": "p_1",
+                "datasetId": "ds_1",
+                "tableId": "tbl_1",
+            }
+        }
+
+        class TableWannabe:
+            pass
+
+        wannabe_other = TableWannabe()
+        wannabe_other._properties = instance._properties
+        wannabe_other.project = "p_1"
+        wannabe_other.dataset_id = "ds_1"
+        wannabe_other.table_id = "tbl_1"
+
+        assert instance != wannabe_other  # Can't fake it.
+        assert instance == mock.ANY  # ...but delegation to other object works.
+
+    def test___eq___project_mismatch(self):
+        instance = self._make_one()
+        instance._properties = {
+            "tableReference": {
+                "projectId": "p_1",
+                "datasetId": "ds_1",
+                "tableId": "tbl_1",
+            }
+        }
+        other = self._make_one()
+        other._properties = {
+            "projectId": "p_2",
+            "datasetId": "ds_1",
+            "tableId": "tbl_1",
+        }
+        assert instance != other
+
+    def test___eq___dataset_mismatch(self):
+        instance = self._make_one()
+        instance._properties = {
+            "tableReference": {
+                "projectId": "p_1",
+                "datasetId": "ds_1",
+                "tableId": "tbl_1",
+            }
+        }
+        other = self._make_one()
+        other._properties = {
+            "tableReference": {
+                "projectId": "p_1",
+                "datasetId": "ds_2",
+                "tableId": "tbl_1",
+            }
+        }
+        assert instance != other
+
+    def test___eq___table_mismatch(self):
+        instance = self._make_one()
+        instance._properties = {
+            "tableReference": {
+                "projectId": "p_1",
+                "datasetId": "ds_1",
+                "tableId": "tbl_1",
+            }
+        }
+        other = self._make_one()
+        other._properties = {
+            "tableReference": {
+                "projectId": "p_1",
+                "datasetId": "ds_1",
+                "tableId": "tbl_2",
+            }
+        }
+        assert instance != other
+
+    def test___eq___equality(self):
+        instance = self._make_one()
+        instance._properties = {
+            "tableReference": {
+                "projectId": "p_1",
+                "datasetId": "ds_1",
+                "tableId": "tbl_1",
+            }
+        }
+        other = self._make_one()
+        other._properties = {
+            "tableReference": {
+                "projectId": "p_1",
+                "datasetId": "ds_1",
+                "tableId": "tbl_1",
+            }
+        }
+        assert instance == other
+
+    def test___hash__set_equality(self):
+        instance_1 = self._make_one()
+        instance_1._properties = {
+            "tableReference": {
+                "projectId": "p_1",
+                "datasetId": "ds_1",
+                "tableId": "tbl_1",
+            }
+        }
+
+        instance_2 = self._make_one()
+        instance_2._properties = {
+            "tableReference": {
+                "projectId": "p_2",
+                "datasetId": "ds_2",
+                "tableId": "tbl_2",
+            }
+        }
+
+        set_one = {instance_1, instance_2}
+        set_two = {instance_1, instance_2}
+        assert set_one == set_two
+
+    def test___hash__sets_not_equal(self):
+        instance_1 = self._make_one()
+        instance_1._properties = {
+            "tableReference": {
+                "projectId": "p_1",
+                "datasetId": "ds_1",
+                "tableId": "tbl_1",
+            }
+        }
+
+        instance_2 = self._make_one()
+        instance_2._properties = {
+            "tableReference": {
+                "projectId": "p_2",
+                "datasetId": "ds_2",
+                "tableId": "tbl_2",
+            }
+        }
+
+        set_one = {instance_1}
+        set_two = {instance_2}
+        assert set_one != set_two
+
+
 class TestTableReference(unittest.TestCase):
     @staticmethod
     def _get_target_class():
@@ -196,55 +379,6 @@ def test_from_string_ignores_default_project(self):
         self.assertEqual(got.dataset_id, "string_dataset")
         self.assertEqual(got.table_id, "string_table")
 
-    def test___eq___wrong_type(self):
-        dataset_ref = DatasetReference("project_1", "dataset_1")
-        table = self._make_one(dataset_ref, "table_1")
-        other = object()
-        self.assertNotEqual(table, other)
-        self.assertEqual(table, mock.ANY)
-
-    def test___eq___project_mismatch(self):
-        dataset = DatasetReference("project_1", "dataset_1")
-        other_dataset = DatasetReference("project_2", "dataset_1")
-        table = self._make_one(dataset, "table_1")
-        other = self._make_one(other_dataset, "table_1")
-        self.assertNotEqual(table, other)
-
-    def test___eq___dataset_mismatch(self):
-        dataset = DatasetReference("project_1", "dataset_1")
-        other_dataset = DatasetReference("project_1", "dataset_2")
-        table = self._make_one(dataset, "table_1")
-        other = self._make_one(other_dataset, "table_1")
-        self.assertNotEqual(table, other)
-
-    def test___eq___table_mismatch(self):
-        dataset = DatasetReference("project_1", "dataset_1")
-        table = self._make_one(dataset, "table_1")
-        other = self._make_one(dataset, "table_2")
-        self.assertNotEqual(table, other)
-
-    def test___eq___equality(self):
-        dataset = DatasetReference("project_1", "dataset_1")
-        table = self._make_one(dataset, "table_1")
-        other = self._make_one(dataset, "table_1")
-        self.assertEqual(table, other)
-
-    def test___hash__set_equality(self):
-        dataset = DatasetReference("project_1", "dataset_1")
-        table1 = self._make_one(dataset, "table1")
-        table2 = self._make_one(dataset, "table2")
-        set_one = {table1, table2}
-        set_two = {table1, table2}
-        self.assertEqual(set_one, set_two)
-
-    def test___hash__not_equals(self):
-        dataset = DatasetReference("project_1", "dataset_1")
-        table1 = self._make_one(dataset, "table1")
-        table2 = self._make_one(dataset, "table2")
-        set_one = {table1}
-        set_two = {table2}
-        self.assertNotEqual(set_one, set_two)
-
     def test___repr__(self):
         dataset = DatasetReference("project1", "dataset1")
         table1 = self._make_one(dataset, "table1")
@@ -558,44 +692,6 @@ def test_num_rows_getter(self):
         with self.assertRaises(ValueError):
             getattr(table, "num_rows")
 
-    def test__eq__wrong_type(self):
-        table = self._make_one("project_foo.dataset_bar.table_baz")
-
-        class TableWannabe:
-            pass
-
-        not_a_table = TableWannabe()
-        not_a_table._properties = table._properties
-
-        assert table != not_a_table  # Can't fake it.
-
-    def test__eq__same_table_basic(self):
-        table_1 = self._make_one("project_foo.dataset_bar.table_baz")
-        table_2 = self._make_one("project_foo.dataset_bar.table_baz")
-        assert table_1 == table_2
-
-    def test__eq__same_table_multiple_properties(self):
-        from google.cloud.bigquery import SchemaField
-
-        table_1 = self._make_one("project_foo.dataset_bar.table_baz")
-        table_1.require_partition_filter = True
-        table_1.labels = {"first": "one", "second": "two"}
-
-        table_1.schema = [
-            SchemaField("name", "STRING", "REQUIRED"),
-            SchemaField("age", "INTEGER", "NULLABLE"),
-        ]
-
-        table_2 = self._make_one("project_foo.dataset_bar.table_baz")
-        table_2.require_partition_filter = True
-        table_2.labels = {"first": "one", "second": "two"}
-        table_2.schema = [
-            SchemaField("name", "STRING", "REQUIRED"),
-            SchemaField("age", "INTEGER", "NULLABLE"),
-        ]
-
-        assert table_1 == table_2
-
     def test__eq__same_table_property_different(self):
         table_1 = self._make_one("project_foo.dataset_bar.table_baz")
         table_1.description = "This is table baz"
@@ -605,12 +701,6 @@ def test__eq__same_table_property_different(self):
 
         assert table_1 == table_2  # Still equal, only table reference is important.
 
-    def test__eq__different_table(self):
-        table_1 = self._make_one("project_foo.dataset_bar.table_baz")
-        table_2 = self._make_one("project_foo.dataset_bar.table_baz_2")
-
-        assert table_1 != table_2
-
     def test_hashable(self):
         table_1 = self._make_one("project_foo.dataset_bar.table_baz")
         table_1.description = "This is a table"
@@ -1584,38 +1674,6 @@ def test_to_api_repr(self):
         table = self._make_one(resource)
         self.assertEqual(table.to_api_repr(), resource)
 
-    def test__eq__wrong_type(self):
-        resource = {
-            "tableReference": {
-                "projectId": "project_foo",
-                "datasetId": "dataset_bar",
-                "tableId": "table_baz",
-            }
-        }
-        table = self._make_one(resource)
-
-        class FakeTableListItem:
-            project = "project_foo"
-            dataset_id = "dataset_bar"
-            table_id = "table_baz"
-
-        not_a_table = FakeTableListItem()
-
-        assert table != not_a_table  # Can't fake it.
-
-    def test__eq__same_table(self):
-        resource = {
-            "tableReference": {
-                "projectId": "project_foo",
-                "datasetId": "dataset_bar",
-                "tableId": "table_baz",
-            }
-        }
-        table_1 = self._make_one(resource)
-        table_2 = self._make_one(resource)
-
-        assert table_1 == table_2
-
     def test__eq__same_table_property_different(self):
         table_ref_resource = {
             "projectId": "project_foo",
@@ -1631,40 +1689,6 @@ def test__eq__same_table_property_different(self):
 
         assert table_1 == table_2  # Still equal, only table reference is important.
 
-    def test__eq__different_table(self):
-        resource_1 = {
-            "tableReference": {
-                "projectId": "project_foo",
-                "datasetId": "dataset_bar",
-                "tableId": "table_baz",
-            }
-        }
-        table_1 = self._make_one(resource_1)
-
-        resource_2 = {
-            "tableReference": {
-                "projectId": "project_foo",
-                "datasetId": "dataset_bar",
-                "tableId": "table_quux",
-            }
-        }
-        table_2 = self._make_one(resource_2)
-
-        assert table_1 != table_2
-
-    def test_hashable(self):
-        resource = {
-            "tableReference": {
-                "projectId": "project_foo",
-                "datasetId": "dataset_bar",
-                "tableId": "table_baz",
-            }
-        }
-        table_item = self._make_one(resource)
-        table_item_2 = self._make_one(resource)
-
-        assert hash(table_item) == hash(table_item_2)
-
 
 class TestTableClassesInterchangeability:
     @staticmethod