Repository: dbt-labs/dbt-core
Pull request: Remove TableComparison and convert existing calls to use dbt.tests.util

resolves #4778

### Description

Remove TableComparison and the dbt.test.tables file. Replace existing calls to TableComparison to functions in dbt.tests.util which were converted from dbt-adapter-tests.  Add some comments.

I fixed a commented out assert in the dbt_run function, and had to update a bunch of tests because of that.

### Checklist

- [x] I have signed the [CLA](https://docs.getdbt.com/docs/contributor-license-agreements)
- [x] I have run this code in development and it appears to resolve the stated issue
- [x] This PR includes tests, or tests are not required/relevant for this PR
- [x] I have added information about my change to be included in the [CHANGELOG](https://github.com/dbt-labs/dbt-core/blob/main/CONTRIBUTING.md#Adding-CHANGELOG-Entry).


Problem statement:
[CT-281] Make obsolete the test compare tables code now in core/dbt/tests/tables.py
As part of the conversion to switching from unittest to pytest, the code in test/integration/base.py that implements the five table comparison assertions was pulled into a separate file, core/dbt/tests/tables.py.  There are better ways to do this, including using macros and data tests. 

Implement the table comparisons in a more dbt-like way, update the new pytest tests to use it, and remove dbt.tests.tables.

Hints from issue discussion:
In particular, there's a lot of overlap between what's in [`tables.py`](https://github.com/dbt-labs/dbt-core/blob/main/core/dbt/tests/tables.py), and what's been implemented to power the (relatively newer) suite of basic adapter tests [here](https://github.com/dbt-labs/dbt-core/blob/ea5a9da71ef71cc13414cd8c2f8097448add5edb/core/dbt/adapters/base/impl.py#L1052-L1141). Where possible, we should opt for:
- defining this functionality on the adapter
- wrapping all SQL in Jinja macros

Other current sources of jankiness, worth a second pass:
- [`run_sql_with_adapter`](https://github.com/dbt-labs/dbt-core/blob/ea5a9da71ef71cc13414cd8c2f8097448add5edb/core/dbt/tests/util.py#L115-L135) + [`adapter.run_sql_for_tests`](https://github.com/dbt-labs/dbt-core/blob/ea5a9da71ef71cc13414cd8c2f8097448add5edb/core/dbt/adapters/sql/impl.py#L222-L246)
- [This `update_rows` business](https://github.com/dbt-labs/dbt-core/blob/ea5a9da71ef71cc13414cd8c2f8097448add5edb/core/dbt/tests/util.py#L261-L327) is interesting from start to finish
work items:
- identify duplicate functionality in table.py vs macros + adapter methods
- convert to using macro and adapter methods where possible
- identify and create missing macro and adapter methods
- update existing tests if necessary

BigQuery will be a bit of a challenge

Commit message: Minor cleanup

Patch to review:
diff --git a/.changes/unreleased/Under the Hood-20220401-154729.yaml b/.changes/unreleased/Under the Hood-20220401-154729.yaml
new file mode 100644
index 00000000000..6556d742b0b
--- /dev/null
+++ b/.changes/unreleased/Under the Hood-20220401-154729.yaml	
@@ -0,0 +1,7 @@
+kind: Under the Hood
+body: Remove TableComparison and convert existing calls to use dbt.tests.util
+time: 2022-04-01T15:47:29.054759-04:00
+custom:
+  Author: gshank
+  Issue: "4778"
+  PR: "4986"
diff --git a/core/dbt/tests/tables.py b/core/dbt/tests/tables.py
deleted file mode 100644
index db0d951c9ba..00000000000
--- a/core/dbt/tests/tables.py
+++ /dev/null
@@ -1,365 +0,0 @@
-from dbt.context import providers
-from unittest.mock import patch
-from contextlib import contextmanager
-from dbt.events.functions import fire_event
-from dbt.events.test_types import IntegrationTestDebug
-
-# This code was copied from the earlier test framework in test/integration/base.py
-# The goal is to vastly simplify this and replace it with calls to macros.
-# For now, we use this to get the tests converted in a more straightforward way.
-# Assertions:
-#   assert_tables_equal  (old: assertTablesEqual)
-#   assert_many_relations_equal  (old: assertManyRelationsEqual)
-#   assert_many_tables_equal  (old: assertManyTablesEqual)
-#   assert_table_does_not_exist  (old: assertTableDoesNotExist)
-#   assert_table_does_exist  (old: assertTableDoesExist)
-
-
-class TableComparison:
-    def __init__(self, adapter, unique_schema, database):
-        self.adapter = adapter
-        self.unique_schema = unique_schema
-        self.default_database = database
-        # TODO: We need to get this from somewhere reasonable
-        if database == "dbtMixedCase":
-            self.quoting = {"database": True, "schema": True, "identifier": True}
-        else:
-            self.quoting = {"database": False, "schema": False, "identifier": False}
-
-    # assertion used in tests
-    def assert_tables_equal(
-        self,
-        table_a,
-        table_b,
-        table_a_schema=None,
-        table_b_schema=None,
-        table_a_db=None,
-        table_b_db=None,
-    ):
-        if table_a_schema is None:
-            table_a_schema = self.unique_schema
-
-        if table_b_schema is None:
-            table_b_schema = self.unique_schema
-
-        if table_a_db is None:
-            table_a_db = self.default_database
-
-        if table_b_db is None:
-            table_b_db = self.default_database
-
-        relation_a = self._make_relation(table_a, table_a_schema, table_a_db)
-        relation_b = self._make_relation(table_b, table_b_schema, table_b_db)
-
-        self._assert_table_columns_equal(relation_a, relation_b)
-
-        sql = self._assert_tables_equal_sql(relation_a, relation_b)
-        result = self.run_sql(sql, fetch="one")
-
-        assert result[0] == 0, "row_count_difference nonzero: " + sql
-        assert result[1] == 0, "num_mismatched nonzero: " + sql
-
-    # assertion used in tests
-    def assert_many_relations_equal(self, relations, default_schema=None, default_database=None):
-        if default_schema is None:
-            default_schema = self.unique_schema
-        if default_database is None:
-            default_database = self.default_database
-
-        specs = []
-        for relation in relations:
-            if not isinstance(relation, (tuple, list)):
-                relation = [relation]
-
-            assert len(relation) <= 3
-
-            if len(relation) == 3:
-                relation = self._make_relation(*relation)
-            elif len(relation) == 2:
-                relation = self._make_relation(relation[0], relation[1], default_database)
-            elif len(relation) == 1:
-                relation = self._make_relation(relation[0], default_schema, default_database)
-            else:
-                raise ValueError("relation must be a sequence of 1, 2, or 3 values")
-
-            specs.append(relation)
-
-        with self.get_connection():
-            column_specs = self.get_many_relation_columns(specs)
-
-        # make sure everyone has equal column definitions
-        first_columns = None
-        for relation in specs:
-            key = (relation.database, relation.schema, relation.identifier)
-            # get a good error here instead of a hard-to-diagnose KeyError
-            assert key in column_specs, f"No columns found for {key}"
-            columns = column_specs[key]
-            if first_columns is None:
-                first_columns = columns
-            else:
-                assert first_columns == columns, f"{str(specs[0])} did not match {str(relation)}"
-
-        # make sure everyone has the same data. if we got here, everyone had
-        # the same column specs!
-        first_relation = None
-        for relation in specs:
-            if first_relation is None:
-                first_relation = relation
-            else:
-                sql = self._assert_tables_equal_sql(
-                    first_relation, relation, columns=first_columns
-                )
-                result = self.run_sql(sql, fetch="one")
-
-                assert result[0] == 0, "row_count_difference nonzero: " + sql
-                assert result[1] == 0, "num_mismatched nonzero: " + sql
-
-    # assertion used in tests
-    def assert_many_tables_equal(self, *args):
-        schema = self.unique_schema
-
-        all_tables = []
-        for table_equivalencies in args:
-            all_tables += list(table_equivalencies)
-
-        all_cols = self.get_table_columns_as_dict(all_tables, schema)
-
-        for table_equivalencies in args:
-            first_table = table_equivalencies[0]
-            first_relation = self._make_relation(first_table)
-
-            # assert that all tables have the same columns
-            base_result = all_cols[first_table]
-            assert len(base_result) > 0
-
-            for other_table in table_equivalencies[1:]:
-                other_result = all_cols[other_table]
-                assert len(other_result) > 0
-                assert base_result == other_result
-
-                other_relation = self._make_relation(other_table)
-                sql = self._assert_tables_equal_sql(
-                    first_relation, other_relation, columns=base_result
-                )
-                result = self.run_sql(sql, fetch="one")
-
-                assert result[0] == 0, "row_count_difference nonzero: " + sql
-                assert result[1] == 0, "num_mismatched nonzero: " + sql
-
-    # assertion used in tests
-    def assert_table_does_not_exist(self, table, schema=None, database=None):
-        columns = self.get_table_columns(table, schema, database)
-        assert len(columns) == 0
-
-    # assertion used in tests
-    def assert_table_does_exist(self, table, schema=None, database=None):
-        columns = self.get_table_columns(table, schema, database)
-
-        assert len(columns) > 0
-
-    # called by assert_tables_equal
-    def _assert_table_columns_equal(self, relation_a, relation_b):
-        table_a_result = self.get_relation_columns(relation_a)
-        table_b_result = self.get_relation_columns(relation_b)
-
-        assert len(table_a_result) == len(table_b_result)
-
-        for a_column, b_column in zip(table_a_result, table_b_result):
-            a_name, a_type, a_size = a_column
-            b_name, b_type, b_size = b_column
-            assert a_name == b_name, "{} vs {}: column '{}' != '{}'".format(
-                relation_a, relation_b, a_name, b_name
-            )
-
-            assert a_type == b_type, "{} vs {}: column '{}' has type '{}' != '{}'".format(
-                relation_a, relation_b, a_name, a_type, b_type
-            )
-
-            assert a_size == b_size, "{} vs {}: column '{}' has size '{}' != '{}'".format(
-                relation_a, relation_b, a_name, a_size, b_size
-            )
-
-    def get_relation_columns(self, relation):
-        with self.get_connection():
-            columns = self.adapter.get_columns_in_relation(relation)
-        return sorted(((c.name, c.dtype, c.char_size) for c in columns), key=lambda x: x[0])
-
-    def get_table_columns(self, table, schema=None, database=None):
-        schema = self.unique_schema if schema is None else schema
-        database = self.default_database if database is None else database
-        relation = self.adapter.Relation.create(
-            database=database,
-            schema=schema,
-            identifier=table,
-            type="table",
-            quote_policy=self.quoting,
-        )
-        return self.get_relation_columns(relation)
-
-    # called by assert_many_table_equal
-    def get_table_columns_as_dict(self, tables, schema=None):
-        col_matrix = self.get_many_table_columns(tables, schema)
-        res = {}
-        for row in col_matrix:
-            table_name = row[0]
-            col_def = row[1:]
-            if table_name not in res:
-                res[table_name] = []
-            res[table_name].append(col_def)
-        return res
-
-    # override for presto
-    @property
-    def column_schema(self):
-        return "table_name, column_name, data_type, character_maximum_length"
-
-    # This should be overridden for Snowflake. Called by get_many_table_columns.
-    def get_many_table_columns_information_schema(self, tables, schema, database=None):
-        columns = self.column_schema
-
-        sql = """
-                select {columns}
-                from {db_string}information_schema.columns
-                where {schema_filter}
-                  and ({table_filter})
-                order by column_name asc"""
-
-        db_string = ""
-        if database:
-            db_string = self.quote_as_configured(database, "database") + "."
-
-        table_filters_s = " OR ".join(
-            _ilike("table_name", table.replace('"', "")) for table in tables
-        )
-        schema_filter = _ilike("table_schema", schema)
-
-        sql = sql.format(
-            columns=columns,
-            schema_filter=schema_filter,
-            table_filter=table_filters_s,
-            db_string=db_string,
-        )
-
-        columns = self.run_sql(sql, fetch="all")
-        return list(map(self.filter_many_columns, columns))
-
-    # Snowflake needs a static char_size
-    def filter_many_columns(self, column):
-        if len(column) == 3:
-            table_name, column_name, data_type = column
-            char_size = None
-        else:
-            table_name, column_name, data_type, char_size = column
-        return (table_name, column_name, data_type, char_size)
-
-    @contextmanager
-    def get_connection(self, name="_test"):
-        """Create a test connection context where all executed macros, etc will
-        use the adapter created in the schema fixture.
-        This allows tests to run normal adapter macros as if reset_adapters()
-        were not called by handle_and_check (for asserts, etc)
-        """
-        with patch.object(providers, "get_adapter", return_value=self.adapter):
-            with self.adapter.connection_named(name):
-                conn = self.adapter.connections.get_thread_connection()
-                yield conn
-
-    def _make_relation(self, identifier, schema=None, database=None):
-        if schema is None:
-            schema = self.unique_schema
-        if database is None:
-            database = self.default_database
-        return self.adapter.Relation.create(
-            database=database, schema=schema, identifier=identifier, quote_policy=self.quoting
-        )
-
-    # called by get_many_relation_columns
-    def get_many_table_columns(self, tables, schema, database=None):
-        result = self.get_many_table_columns_information_schema(tables, schema, database)
-        result.sort(key=lambda x: "{}.{}".format(x[0], x[1]))
-        return result
-
-    # called by assert_many_relations_equal
-    def get_many_relation_columns(self, relations):
-        """Returns a dict of (datbase, schema) -> (dict of (table_name -> list of columns))"""
-        schema_fqns = {}
-        for rel in relations:
-            this_schema = schema_fqns.setdefault((rel.database, rel.schema), [])
-            this_schema.append(rel.identifier)
-
-        column_specs = {}
-        for key, tables in schema_fqns.items():
-            database, schema = key
-            columns = self.get_many_table_columns(tables, schema, database=database)
-            table_columns = {}
-            for col in columns:
-                table_columns.setdefault(col[0], []).append(col[1:])
-            for rel_name, columns in table_columns.items():
-                key = (database, schema, rel_name)
-                column_specs[key] = columns
-
-        return column_specs
-
-    def _assert_tables_equal_sql(self, relation_a, relation_b, columns=None):
-        if columns is None:
-            columns = self.get_relation_columns(relation_a)
-        column_names = [c[0] for c in columns]
-        sql = self.adapter.get_rows_different_sql(relation_a, relation_b, column_names)
-        return sql
-
-    # This duplicates code in the TestProjInfo class.
-    def run_sql(self, sql, fetch=None):
-        if sql.strip() == "":
-            return
-        # substitute schema and database in sql
-        adapter = self.adapter
-        kwargs = {
-            "schema": self.unique_schema,
-            "database": adapter.quote(self.default_database),
-        }
-        sql = sql.format(**kwargs)
-
-        with self.get_connection("__test") as conn:
-            msg = f'test connection "{conn.name}" executing: {sql}'
-            fire_event(IntegrationTestDebug(msg=msg))
-            with conn.handle.cursor() as cursor:
-                try:
-                    cursor.execute(sql)
-                    conn.handle.commit()
-                    conn.handle.commit()
-                    if fetch == "one":
-                        return cursor.fetchone()
-                    elif fetch == "all":
-                        return cursor.fetchall()
-                    else:
-                        return
-                except BaseException as e:
-                    if conn.handle and not getattr(conn.handle, "closed", True):
-                        conn.handle.rollback()
-                    print(sql)
-                    print(e)
-                    raise
-                finally:
-                    conn.transaction_open = False
-
-    def get_tables_in_schema(self):
-        sql = """
-                select table_name,
-                        case when table_type = 'BASE TABLE' then 'table'
-                             when table_type = 'VIEW' then 'view'
-                             else table_type
-                        end as materialization
-                from information_schema.tables
-                where {}
-                order by table_name
-                """
-
-        sql = sql.format(_ilike("table_schema", self.unique_schema))
-        result = self.run_sql(sql, fetch="all")
-
-        return {model_name: materialization for (model_name, materialization) in result}
-
-
-# needs overriding for presto
-def _ilike(target, value):
-    return "{} ilike '{}'".format(target, value)
diff --git a/core/dbt/tests/util.py b/core/dbt/tests/util.py
index 7f132f4c7a0..abc00b84ad0 100644
--- a/core/dbt/tests/util.py
+++ b/core/dbt/tests/util.py
@@ -4,6 +4,7 @@
 import json
 import warnings
 from typing import List
+from contextlib import contextmanager
 
 from dbt.main import handle_and_check
 from dbt.logger import log_manager
@@ -13,6 +14,35 @@
 from dbt.context import providers
 from unittest.mock import patch
 
+# =============================================================================
+# Test utilities
+#   run_dbt
+#   run_dbt_and_capture
+#   get_manifest
+#   copy_file
+#   rm_file
+#   write_file
+#   read_file
+#   get_artifact
+#   update_config_file
+#   get_unique_ids_in_results
+#   check_results_nodes_by_name
+#   check_result_nodes_by_unique_id
+
+# SQL related utilities
+#   run_sql_with_adapter
+#   relation_from_name
+#   check_relation_types (table/view)
+#   check_relations_equal
+#   check_relations_equal_with_relations
+#   check_table_does_exist
+#   check_table_does_not_exist
+#   get_relation_columns
+#   update_rows
+#      generate_update_clause
+
+# =============================================================================
+
 
 # This is used in pytest tests to run dbt
 def run_dbt(args: List[str] = None, expect_pass=True):
@@ -28,7 +58,7 @@ def run_dbt(args: List[str] = None, expect_pass=True):
 
     print("\n\nInvoking dbt with {}".format(args))
     res, success = handle_and_check(args)
-    #   assert success == expect_pass, "dbt exit state did not match expected"
+    assert success == expect_pass, "dbt exit state did not match expected"
     return res
 
 
@@ -112,6 +142,37 @@ def update_config_file(updates, *paths):
     write_file(new_yaml, *paths)
 
 
+def get_unique_ids_in_results(results):
+    unique_ids = []
+    for result in results:
+        unique_ids.append(result.node.unique_id)
+    return unique_ids
+
+
+def check_result_nodes_by_name(results, names):
+    result_names = []
+    for result in results:
+        result_names.append(result.node.name)
+    assert set(names) == set(result_names)
+
+
+def check_result_nodes_by_unique_id(results, unique_ids):
+    result_unique_ids = []
+    for result in results:
+        result_unique_ids.append(result.node.unique_id)
+    assert set(unique_ids) == set(result_unique_ids)
+
+
+class TestProcessingException(Exception):
+    pass
+
+
+# Testing utilties that use adapter code
+
+# Uses:
+#    adapter.config.credentials
+#    adapter.quote
+#    adapter.run_sql_for_tests
 def run_sql_with_adapter(adapter, sql, fetch=None):
     if sql.strip() == "":
         return
@@ -124,21 +185,18 @@ def run_sql_with_adapter(adapter, sql, fetch=None):
 
     msg = f'test connection "__test" executing: {sql}'
     fire_event(IntegrationTestDebug(msg=msg))
-    # Since the 'adapter' in dbt.adapters.factory may have been replaced by execution
-    # of dbt commands since the test 'adapter' was created, we patch the 'get_adapter' call in
-    # dbt.context.providers, so that macros that are called refer to this test adapter.
-    # This allows tests to run normal adapter macros as if reset_adapters() were not
-    # called by handle_and_check (for asserts, etc).
-    with patch.object(providers, "get_adapter", return_value=adapter):
-        with adapter.connection_named("__test"):
-            conn = adapter.connections.get_thread_connection()
-            return adapter.run_sql_for_tests(sql, fetch, conn)
-
-
-class TestProcessingException(Exception):
-    pass
-
-
+    with get_connection(adapter) as conn:
+        return adapter.run_sql_for_tests(sql, fetch, conn)
+
+
+# Get a Relation object from the identifer (name of table/view).
+# Uses the default database and schema. If you need a relation
+# with a different schema, it should be constructed in the test.
+# Uses:
+#    adapter.Relation
+#    adapter.config.credentials
+#    Relation.get_default_quote_policy
+#    Relation.get_default_include_policy
 def relation_from_name(adapter, name: str):
     """reverse-engineer a relation from a given name and
     the adapter. The relation name is split by the '.' character.
@@ -171,6 +229,10 @@ def relation_from_name(adapter, name: str):
     return relation
 
 
+# Ensure that models with different materialiations have the
+# corrent table/view.
+# Uses:
+#   adapter.list_relations_without_caching
 def check_relation_types(adapter, relation_to_type):
     """
     Relation name to table/view
@@ -189,8 +251,7 @@ def check_relation_types(adapter, relation_to_type):
         expected_relation_values[relation] = value
         schemas.add(relation.without_identifier())
 
-    with patch.object(providers, "get_adapter", return_value=adapter):
-        with adapter.connection_named("__test"):
+        with get_connection(adapter):
             for schema in schemas:
                 found_relations.extend(adapter.list_relations_without_caching(schema))
 
@@ -204,60 +265,60 @@ def check_relation_types(adapter, relation_to_type):
                 )
 
 
+# Replaces assertTablesEqual. assertManyTablesEqual can be replaced
+# by doing a separate call for each set of tables/relations.
 def check_relations_equal(adapter, relation_names):
     if len(relation_names) < 2:
         raise TestProcessingException(
             "Not enough relations to compare",
         )
     relations = [relation_from_name(adapter, name) for name in relation_names]
-
-    with patch.object(providers, "get_adapter", return_value=adapter):
-        with adapter.connection_named("_test"):
-            basis, compares = relations[0], relations[1:]
-            columns = [c.name for c in adapter.get_columns_in_relation(basis)]
-
-            for relation in compares:
-                sql = adapter.get_rows_different_sql(basis, relation, column_names=columns)
-                _, tbl = adapter.execute(sql, fetch=True)
-                num_rows = len(tbl)
-                assert (
-                    num_rows == 1
-                ), f"Invalid sql query from get_rows_different_sql: incorrect number of rows ({num_rows})"
-                num_cols = len(tbl[0])
-                assert (
-                    num_cols == 2
-                ), f"Invalid sql query from get_rows_different_sql: incorrect number of cols ({num_cols})"
-                row_count_difference = tbl[0][0]
-                assert (
-                    row_count_difference == 0
-                ), f"Got {row_count_difference} difference in row count betwen {basis} and {relation}"
-                rows_mismatched = tbl[0][1]
-                assert (
-                    rows_mismatched == 0
-                ), f"Got {rows_mismatched} different rows between {basis} and {relation}"
-
-
-def get_unique_ids_in_results(results):
-    unique_ids = []
-    for result in results:
-        unique_ids.append(result.node.unique_id)
-    return unique_ids
-
-
-def check_result_nodes_by_name(results, names):
-    result_names = []
-    for result in results:
-        result_names.append(result.node.name)
-    assert set(names) == set(result_names)
-
-
-def check_result_nodes_by_unique_id(results, unique_ids):
-    result_unique_ids = []
-    for result in results:
-        result_unique_ids.append(result.node.unique_id)
-    assert set(unique_ids) == set(result_unique_ids)
-
-
+    return check_relations_equal_with_relations(adapter, relations)
+
+
+# This can be used when checking relations in different schemas, by supplying
+# a list of relations. Called by 'check_relations_equal'.
+# Uses:
+#    adapter.get_columns_in_relation
+#    adapter.get_rows_different_sql
+#    adapter.execute
+def check_relations_equal_with_relations(adapter, relations):
+
+    with get_connection(adapter):
+        basis, compares = relations[0], relations[1:]
+        # Skip columns starting with "dbt_" because we don't want to
+        # compare those, since they are time sensitive
+        column_names = [
+            c.name
+            for c in adapter.get_columns_in_relation(basis)
+            if not c.name.lower().startswith("dbt_")
+        ]
+
+        for relation in compares:
+            sql = adapter.get_rows_different_sql(basis, relation, column_names=column_names)
+            _, tbl = adapter.execute(sql, fetch=True)
+            num_rows = len(tbl)
+            assert (
+                num_rows == 1
+            ), f"Invalid sql query from get_rows_different_sql: incorrect number of rows ({num_rows})"
+            num_cols = len(tbl[0])
+            assert (
+                num_cols == 2
+            ), f"Invalid sql query from get_rows_different_sql: incorrect number of cols ({num_cols})"
+            row_count_difference = tbl[0][0]
+            assert (
+                row_count_difference == 0
+            ), f"Got {row_count_difference} difference in row count betwen {basis} and {relation}"
+            rows_mismatched = tbl[0][1]
+            assert (
+                rows_mismatched == 0
+            ), f"Got {rows_mismatched} different rows between {basis} and {relation}"
+
+
+# Uses:
+#    adapter.update_column_sql
+#    adapter.execute
+#    adapter.commit_if_has_connection
 def update_rows(adapter, update_rows_config):
     """
     {
@@ -284,18 +345,21 @@ def update_rows(adapter, update_rows_config):
     dst_col = update_rows_config["dst_col"]
     relation = relation_from_name(adapter, name)
 
-    with patch.object(providers, "get_adapter", return_value=adapter):
-        with adapter.connection_named("_test"):
-            sql = adapter.update_column_sql(
-                dst_name=str(relation),
-                dst_column=dst_col,
-                clause=clause,
-                where_clause=where,
-            )
-            adapter.execute(sql, auto_begin=True)
-            adapter.commit_if_has_connection()
+    with get_connection(adapter):
+        sql = adapter.update_column_sql(
+            dst_name=str(relation),
+            dst_column=dst_col,
+            clause=clause,
+            where_clause=where,
+        )
+        adapter.execute(sql, auto_begin=True)
+        adapter.commit_if_has_connection()
 
 
+# This is called by the 'update_rows' function.
+# Uses:
+#    adapter.timestamp_add_sql
+#    adapter.string_add_sql
 def generate_update_clause(adapter, clause) -> str:
     """
     Called by update_rows function. Expects the "clause" dictionary
@@ -311,9 +375,8 @@ def generate_update_clause(adapter, clause) -> str:
             raise TestProcessingException("Invalid update_rows clause: no src_col")
         add_to = clause["src_col"]
         kwargs = {k: v for k, v in clause.items() if k in ("interval", "number")}
-        with patch.object(providers, "get_adapter", return_value=adapter):
-            with adapter.connection_named("_test"):
-                return adapter.timestamp_add_sql(add_to=add_to, **kwargs)
+        with get_connection(adapter):
+            return adapter.timestamp_add_sql(add_to=add_to, **kwargs)
     elif clause_type == "add_string":
         for key in ["src_col", "value"]:
             if key not in clause:
@@ -321,7 +384,38 @@ def generate_update_clause(adapter, clause) -> str:
         src_col = clause["src_col"]
         value = clause["value"]
         location = clause.get("location", "append")
-        with patch.object(providers, "get_adapter", return_value=adapter):
-            with adapter.connection_named("_test"):
-                return adapter.string_add_sql(src_col, value, location)
+        with get_connection(adapter):
+            return adapter.string_add_sql(src_col, value, location)
     return ""
+
+
+@contextmanager
+def get_connection(adapter, name="_test"):
+    # Since the 'adapter' in dbt.adapters.factory may have been replaced by execution
+    # of dbt commands since the test 'adapter' was created, we patch the 'get_adapter' call in
+    # dbt.context.providers, so that macros that are called refer to this test adapter.
+    # This allows tests to run normal adapter macros as if reset_adapters() were not
+    # called by handle_and_check (for asserts, etc).
+    with patch.object(providers, "get_adapter", return_value=adapter):
+        with adapter.connection_named(name):
+            conn = adapter.connections.get_thread_connection()
+            yield conn
+
+
+# Uses:
+#    adapter.get_columns_in_relation
+def get_relation_columns(adapter, name):
+    relation = relation_from_name(adapter, name)
+    with get_connection(adapter):
+        columns = adapter.get_columns_in_relation(relation)
+        return sorted(((c.name, c.dtype, c.char_size) for c in columns), key=lambda x: x[0])
+
+
+def check_table_does_not_exist(adapter, name):
+    columns = get_relation_columns(adapter, name)
+    assert len(columns) == 0
+
+
+def check_table_does_exist(adapter, name):
+    columns = get_relation_columns(adapter, name)
+    assert len(columns) > 0
diff --git a/tests/adapter/dbt/tests/adapter/basic/test_singular_tests.py b/tests/adapter/dbt/tests/adapter/basic/test_singular_tests.py
index 9ed329aa1e0..4cbf70094a3 100644
--- a/tests/adapter/dbt/tests/adapter/basic/test_singular_tests.py
+++ b/tests/adapter/dbt/tests/adapter/basic/test_singular_tests.py
@@ -20,7 +20,7 @@ def project_config_update(self):
 
     def test_singular_tests(self, project):
         # test command
-        results = run_dbt(["test"])
+        results = run_dbt(["test"], expect_pass=False)
         assert len(results) == 2
 
         # We have the right result nodes
diff --git a/tests/adapter/dbt/tests/adapter/basic/test_singular_tests_ephemeral.py b/tests/adapter/dbt/tests/adapter/basic/test_singular_tests_ephemeral.py
index 395a84e712a..caaa629a7a2 100644
--- a/tests/adapter/dbt/tests/adapter/basic/test_singular_tests_ephemeral.py
+++ b/tests/adapter/dbt/tests/adapter/basic/test_singular_tests_ephemeral.py
@@ -46,7 +46,7 @@ def test_singular_tests_ephemeral(self, project):
         check_result_nodes_by_name(results, ["base"])
 
         # Check results from test command
-        results = run_dbt(["test"])
+        results = run_dbt(["test"], expect_pass=False)
         assert len(results) == 2
         check_result_nodes_by_name(results, ["passing", "failing"])
 
diff --git a/tests/functional/basic/test_copy_uppercase.py b/tests/functional/basic/test_copy_uppercase.py
index 5b0bc464aae..922d67bfe67 100644
--- a/tests/functional/basic/test_copy_uppercase.py
+++ b/tests/functional/basic/test_copy_uppercase.py
@@ -1,6 +1,5 @@
 import pytest
-from dbt.tests.tables import TableComparison
-from dbt.tests.util import run_dbt, read_file
+from dbt.tests.util import run_dbt, read_file, check_relations_equal
 
 from tests.functional.basic.test_simple_copy import (
     advanced_incremental_sql,
@@ -76,9 +75,6 @@ def test_simple_copy_uppercase(project):
     results = run_dbt()
     assert len(results) == 7
 
-    table_comp = TableComparison(
-        adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-    )
-    table_comp.assert_many_tables_equal(
-        ["seed", "VIEW_MODEL", "INCREMENTAL", "MATERIALIZED", "GET_AND_REF"]
+    check_relations_equal(
+        project.adapter, ["seed", "VIEW_MODEL", "INCREMENTAL", "MATERIALIZED", "GET_AND_REF"]
     )
diff --git a/tests/functional/basic/test_simple_copy.py b/tests/functional/basic/test_simple_copy.py
index d47584265ef..e73ecd22588 100644
--- a/tests/functional/basic/test_simple_copy.py
+++ b/tests/functional/basic/test_simple_copy.py
@@ -1,6 +1,5 @@
 import pytest
-from dbt.tests.util import run_dbt, copy_file, read_file
-from dbt.tests.tables import TableComparison
+from dbt.tests.util import run_dbt, copy_file, read_file, check_relations_equal
 
 # advanced_incremental.sql
 advanced_incremental_sql = """
@@ -163,11 +162,8 @@ def test_simple_copy(project, test_data_dir):
     # Run the project and ensure that all the models loaded
     results = run_dbt()
     assert len(results) == 7
-    table_comp = TableComparison(
-        adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-    )
-    table_comp.assert_many_tables_equal(
-        ["seed", "view_model", "incremental", "materialized", "get_and_ref"]
+    check_relations_equal(
+        project.adapter, ["seed", "view_model", "incremental", "materialized", "get_and_ref"]
     )
 
     # Change the seed.csv file and see if everything is the same, i.e. everything has been updated
@@ -176,8 +172,8 @@ def test_simple_copy(project, test_data_dir):
     assert len(results) == 1
     results = run_dbt()
     assert len(results) == 7
-    table_comp.assert_many_tables_equal(
-        ["seed", "view_model", "incremental", "materialized", "get_and_ref"]
+    check_relations_equal(
+        project.adapter, ["seed", "view_model", "incremental", "materialized", "get_and_ref"]
     )
 
 
diff --git a/tests/functional/basic/test_simple_reference.py b/tests/functional/basic/test_simple_reference.py
index 67f4d6340b7..9207ffc29a0 100644
--- a/tests/functional/basic/test_simple_reference.py
+++ b/tests/functional/basic/test_simple_reference.py
@@ -1,6 +1,5 @@
 import pytest
-from dbt.tests.util import run_dbt, copy_file, read_file
-from dbt.tests.tables import TableComparison
+from dbt.tests.util import run_dbt, copy_file, read_file, check_relations_equal
 
 
 ephemeral_copy_sql = """
@@ -168,21 +167,17 @@ def test_simple_reference(project):
     results = run_dbt()
     assert len(results) == 8
 
-    table_comp = TableComparison(
-        adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-    )
-
     # Copies should match
-    table_comp.assert_tables_equal("users", "incremental_copy")
-    table_comp.assert_tables_equal("users", "materialized_copy")
-    table_comp.assert_tables_equal("users", "view_copy")
+    check_relations_equal(project.adapter, ["users", "incremental_copy"])
+    check_relations_equal(project.adapter, ["users", "materialized_copy"])
+    check_relations_equal(project.adapter, ["users", "view_copy"])
 
     # Summaries should match
-    table_comp.assert_tables_equal("summary_expected", "incremental_summary")
-    table_comp.assert_tables_equal("summary_expected", "materialized_summary")
-    table_comp.assert_tables_equal("summary_expected", "view_summary")
-    table_comp.assert_tables_equal("summary_expected", "ephemeral_summary")
-    table_comp.assert_tables_equal("summary_expected", "view_using_ref")
+    check_relations_equal(project.adapter, ["summary_expected", "incremental_summary"])
+    check_relations_equal(project.adapter, ["summary_expected", "materialized_summary"])
+    check_relations_equal(project.adapter, ["summary_expected", "view_summary"])
+    check_relations_equal(project.adapter, ["summary_expected", "ephemeral_summary"])
+    check_relations_equal(project.adapter, ["summary_expected", "view_using_ref"])
 
     # update the seed files and run seed
     copy_file(
@@ -201,15 +196,15 @@ def test_simple_reference(project):
     assert len(results) == 8
 
     # Copies should match
-    table_comp.assert_tables_equal("users", "incremental_copy")
-    table_comp.assert_tables_equal("users", "materialized_copy")
-    table_comp.assert_tables_equal("users", "view_copy")
+    check_relations_equal(project.adapter, ["users", "incremental_copy"])
+    check_relations_equal(project.adapter, ["users", "materialized_copy"])
+    check_relations_equal(project.adapter, ["users", "view_copy"])
 
     # Summaries should match
-    table_comp.assert_tables_equal("summary_expected", "incremental_summary")
-    table_comp.assert_tables_equal("summary_expected", "materialized_summary")
-    table_comp.assert_tables_equal("summary_expected", "view_summary")
-    table_comp.assert_tables_equal("summary_expected", "ephemeral_summary")
+    check_relations_equal(project.adapter, ["summary_expected", "incremental_summary"])
+    check_relations_equal(project.adapter, ["summary_expected", "materialized_summary"])
+    check_relations_equal(project.adapter, ["summary_expected", "view_summary"])
+    check_relations_equal(project.adapter, ["summary_expected", "ephemeral_summary"])
 
 
 def test_simple_reference_with_models_and_children(project):
@@ -220,16 +215,12 @@ def test_simple_reference_with_models_and_children(project):
     results = run_dbt(["run", "--models", "materialized_copy+", "ephemeral_copy+"])
     assert len(results) == 3
 
-    table_comp = TableComparison(
-        adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-    )
-
     # Copies should match
-    table_comp.assert_tables_equal("users", "materialized_copy")
+    check_relations_equal(project.adapter, ["users", "materialized_copy"])
 
     # Summaries should match
-    table_comp.assert_tables_equal("summary_expected", "materialized_summary")
-    table_comp.assert_tables_equal("summary_expected", "ephemeral_summary")
+    check_relations_equal(project.adapter, ["summary_expected", "materialized_summary"])
+    check_relations_equal(project.adapter, ["summary_expected", "ephemeral_summary"])
 
     created_tables = project.get_tables_in_schema()
 
@@ -260,10 +251,7 @@ def test_simple_ref_with_models(project):
     assert len(results) == 1
 
     # Copies should match
-    table_comp = TableComparison(
-        adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-    )
-    table_comp.assert_tables_equal("users", "materialized_copy")
+    check_relations_equal(project.adapter, ["users", "materialized_copy"])
 
     created_tables = project.get_tables_in_schema()
     assert "materialized_copy" in created_tables
diff --git a/tests/functional/basic/test_varchar_widening.py b/tests/functional/basic/test_varchar_widening.py
index 92bca533077..3e74629adea 100644
--- a/tests/functional/basic/test_varchar_widening.py
+++ b/tests/functional/basic/test_varchar_widening.py
@@ -1,7 +1,6 @@
 import pytest
 import os
-from dbt.tests.util import run_dbt
-from dbt.tests.tables import TableComparison
+from dbt.tests.util import run_dbt, check_relations_equal
 
 
 incremental_sql = """
@@ -43,12 +42,8 @@ def test_varchar_widening(project):
     results = run_dbt(["run"])
     assert len(results) == 2
 
-    table_comp = TableComparison(
-        adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-    )
-
-    table_comp.assert_tables_equal("seed", "incremental")
-    table_comp.assert_tables_equal("seed", "materialized")
+    check_relations_equal(project.adapter, ["seed", "incremental"])
+    check_relations_equal(project.adapter, ["seed", "materialized"])
 
     path = os.path.join(project.test_data_dir, "varchar300_seed.sql")
     project.run_sql_file(path)
@@ -56,5 +51,5 @@ def test_varchar_widening(project):
     results = run_dbt(["run"])
     assert len(results) == 2
 
-    table_comp.assert_tables_equal("seed", "incremental")
-    table_comp.assert_tables_equal("seed", "materialized")
+    check_relations_equal(project.adapter, ["seed", "incremental"])
+    check_relations_equal(project.adapter, ["seed", "materialized"])
diff --git a/tests/functional/graph_selection/data/seed.csv b/tests/functional/graph_selection/data/seed.csv
index 640af6c4ee6..b77dad1bdf4 100644
--- a/tests/functional/graph_selection/data/seed.csv
+++ b/tests/functional/graph_selection/data/seed.csv
@@ -1,101 +1,101 @@
-id,first_name,last_name,email,gender,ip_address
-1,Jack,Hunter,jhunter0@pbs.org,Male,59.80.20.168
-2,Kathryn,Walker,kwalker1@ezinearticles.com,Female,194.121.179.35
-3,Gerald,Ryan,gryan2@com.com,Male,11.3.212.243
-4,Bonnie,Spencer,bspencer3@ameblo.jp,Female,216.32.196.175
-5,Harold,Taylor,htaylor4@people.com.cn,Male,253.10.246.136
-6,Jacqueline,Griffin,jgriffin5@t.co,Female,16.13.192.220
-7,Wanda,Arnold,warnold6@google.nl,Female,232.116.150.64
-8,Craig,Ortiz,cortiz7@sciencedaily.com,Male,199.126.106.13
-9,Gary,Day,gday8@nih.gov,Male,35.81.68.186
-10,Rose,Wright,rwright9@yahoo.co.jp,Female,236.82.178.100
-11,Raymond,Kelley,rkelleya@fc2.com,Male,213.65.166.67
-12,Gerald,Robinson,grobinsonb@disqus.com,Male,72.232.194.193
-13,Mildred,Martinez,mmartinezc@samsung.com,Female,198.29.112.5
-14,Dennis,Arnold,darnoldd@google.com,Male,86.96.3.250
-15,Judy,Gray,jgraye@opensource.org,Female,79.218.162.245
-16,Theresa,Garza,tgarzaf@epa.gov,Female,21.59.100.54
-17,Gerald,Robertson,grobertsong@csmonitor.com,Male,131.134.82.96
-18,Philip,Hernandez,phernandezh@adobe.com,Male,254.196.137.72
-19,Julia,Gonzalez,jgonzalezi@cam.ac.uk,Female,84.240.227.174
-20,Andrew,Davis,adavisj@patch.com,Male,9.255.67.25
-21,Kimberly,Harper,kharperk@foxnews.com,Female,198.208.120.253
-22,Mark,Martin,mmartinl@marketwatch.com,Male,233.138.182.153
-23,Cynthia,Ruiz,cruizm@google.fr,Female,18.178.187.201
-24,Samuel,Carroll,scarrolln@youtu.be,Male,128.113.96.122
-25,Jennifer,Larson,jlarsono@vinaora.com,Female,98.234.85.95
-26,Ashley,Perry,aperryp@rakuten.co.jp,Female,247.173.114.52
-27,Howard,Rodriguez,hrodriguezq@shutterfly.com,Male,231.188.95.26
-28,Amy,Brooks,abrooksr@theatlantic.com,Female,141.199.174.118
-29,Louise,Warren,lwarrens@adobe.com,Female,96.105.158.28
-30,Tina,Watson,twatsont@myspace.com,Female,251.142.118.177
-31,Janice,Kelley,jkelleyu@creativecommons.org,Female,239.167.34.233
-32,Terry,Mccoy,tmccoyv@bravesites.com,Male,117.201.183.203
-33,Jeffrey,Morgan,jmorganw@surveymonkey.com,Male,78.101.78.149
-34,Louis,Harvey,lharveyx@sina.com.cn,Male,51.50.0.167
-35,Philip,Miller,pmillery@samsung.com,Male,103.255.222.110
-36,Willie,Marshall,wmarshallz@ow.ly,Male,149.219.91.68
-37,Patrick,Lopez,plopez10@redcross.org,Male,250.136.229.89
-38,Adam,Jenkins,ajenkins11@harvard.edu,Male,7.36.112.81
-39,Benjamin,Cruz,bcruz12@linkedin.com,Male,32.38.98.15
-40,Ruby,Hawkins,rhawkins13@gmpg.org,Female,135.171.129.255
-41,Carlos,Barnes,cbarnes14@a8.net,Male,240.197.85.140
-42,Ruby,Griffin,rgriffin15@bravesites.com,Female,19.29.135.24
-43,Sean,Mason,smason16@icq.com,Male,159.219.155.249
-44,Anthony,Payne,apayne17@utexas.edu,Male,235.168.199.218
-45,Steve,Cruz,scruz18@pcworld.com,Male,238.201.81.198
-46,Anthony,Garcia,agarcia19@flavors.me,Male,25.85.10.18
-47,Doris,Lopez,dlopez1a@sphinn.com,Female,245.218.51.238
-48,Susan,Nichols,snichols1b@freewebs.com,Female,199.99.9.61
-49,Wanda,Ferguson,wferguson1c@yahoo.co.jp,Female,236.241.135.21
-50,Andrea,Pierce,apierce1d@google.co.uk,Female,132.40.10.209
-51,Lawrence,Phillips,lphillips1e@jugem.jp,Male,72.226.82.87
-52,Judy,Gilbert,jgilbert1f@multiply.com,Female,196.250.15.142
-53,Eric,Williams,ewilliams1g@joomla.org,Male,222.202.73.126
-54,Ralph,Romero,rromero1h@sogou.com,Male,123.184.125.212
-55,Jean,Wilson,jwilson1i@ocn.ne.jp,Female,176.106.32.194
-56,Lori,Reynolds,lreynolds1j@illinois.edu,Female,114.181.203.22
-57,Donald,Moreno,dmoreno1k@bbc.co.uk,Male,233.249.97.60
-58,Steven,Berry,sberry1l@eepurl.com,Male,186.193.50.50
-59,Theresa,Shaw,tshaw1m@people.com.cn,Female,120.37.71.222
-60,John,Stephens,jstephens1n@nationalgeographic.com,Male,191.87.127.115
-61,Richard,Jacobs,rjacobs1o@state.tx.us,Male,66.210.83.155
-62,Andrew,Lawson,alawson1p@over-blog.com,Male,54.98.36.94
-63,Peter,Morgan,pmorgan1q@rambler.ru,Male,14.77.29.106
-64,Nicole,Garrett,ngarrett1r@zimbio.com,Female,21.127.74.68
-65,Joshua,Kim,jkim1s@edublogs.org,Male,57.255.207.41
-66,Ralph,Roberts,rroberts1t@people.com.cn,Male,222.143.131.109
-67,George,Montgomery,gmontgomery1u@smugmug.com,Male,76.75.111.77
-68,Gerald,Alvarez,galvarez1v@flavors.me,Male,58.157.186.194
-69,Donald,Olson,dolson1w@whitehouse.gov,Male,69.65.74.135
-70,Carlos,Morgan,cmorgan1x@pbs.org,Male,96.20.140.87
-71,Aaron,Stanley,astanley1y@webnode.com,Male,163.119.217.44
-72,Virginia,Long,vlong1z@spiegel.de,Female,204.150.194.182
-73,Robert,Berry,rberry20@tripadvisor.com,Male,104.19.48.241
-74,Antonio,Brooks,abrooks21@unesco.org,Male,210.31.7.24
-75,Ruby,Garcia,rgarcia22@ovh.net,Female,233.218.162.214
-76,Jack,Hanson,jhanson23@blogtalkradio.com,Male,31.55.46.199
-77,Kathryn,Nelson,knelson24@walmart.com,Female,14.189.146.41
-78,Jason,Reed,jreed25@printfriendly.com,Male,141.189.89.255
-79,George,Coleman,gcoleman26@people.com.cn,Male,81.189.221.144
-80,Rose,King,rking27@ucoz.com,Female,212.123.168.231
-81,Johnny,Holmes,jholmes28@boston.com,Male,177.3.93.188
-82,Katherine,Gilbert,kgilbert29@altervista.org,Female,199.215.169.61
-83,Joshua,Thomas,jthomas2a@ustream.tv,Male,0.8.205.30
-84,Julie,Perry,jperry2b@opensource.org,Female,60.116.114.192
-85,Richard,Perry,rperry2c@oracle.com,Male,181.125.70.232
-86,Kenneth,Ruiz,kruiz2d@wikimedia.org,Male,189.105.137.109
-87,Jose,Morgan,jmorgan2e@webnode.com,Male,101.134.215.156
-88,Donald,Campbell,dcampbell2f@goo.ne.jp,Male,102.120.215.84
-89,Debra,Collins,dcollins2g@uol.com.br,Female,90.13.153.235
-90,Jesse,Johnson,jjohnson2h@stumbleupon.com,Male,225.178.125.53
-91,Elizabeth,Stone,estone2i@histats.com,Female,123.184.126.221
-92,Angela,Rogers,arogers2j@goodreads.com,Female,98.104.132.187
-93,Emily,Dixon,edixon2k@mlb.com,Female,39.190.75.57
-94,Albert,Scott,ascott2l@tinypic.com,Male,40.209.13.189
-95,Barbara,Peterson,bpeterson2m@ow.ly,Female,75.249.136.180
-96,Adam,Greene,agreene2n@fastcompany.com,Male,184.173.109.144
-97,Earl,Sanders,esanders2o@hc360.com,Male,247.34.90.117
-98,Angela,Brooks,abrooks2p@mtv.com,Female,10.63.249.126
-99,Harold,Foster,hfoster2q@privacy.gov.au,Male,139.214.40.244
-100,Carl,Meyer,cmeyer2r@disqus.com,Male,204.117.7.88
+id,first_name,last_name,email,gender,ip_address,updated_at
+1,Jack,Hunter,jhunter0@pbs.org,Male,59.80.20.168,2015-12-24 12:19:28
+2,Kathryn,Walker,kwalker1@ezinearticles.com,Female,194.121.179.35,2015-12-24 12:19:28
+3,Gerald,Ryan,gryan2@com.com,Male,11.3.212.243,2015-12-24 12:19:28
+4,Bonnie,Spencer,bspencer3@ameblo.jp,Female,216.32.196.175,2015-12-24 12:19:28
+5,Harold,Taylor,htaylor4@people.com.cn,Male,253.10.246.136,2015-12-24 12:19:28
+6,Jacqueline,Griffin,jgriffin5@t.co,Female,16.13.192.220,2015-12-24 12:19:28
+7,Wanda,Arnold,warnold6@google.nl,Female,232.116.150.64,2015-12-24 12:19:28
+8,Craig,Ortiz,cortiz7@sciencedaily.com,Male,199.126.106.13,2015-12-24 12:19:28
+9,Gary,Day,gday8@nih.gov,Male,35.81.68.186,2015-12-24 12:19:28
+10,Rose,Wright,rwright9@yahoo.co.jp,Female,236.82.178.100,2015-12-24 12:19:28
+11,Raymond,Kelley,rkelleya@fc2.com,Male,213.65.166.67,2015-12-24 12:19:28
+12,Gerald,Robinson,grobinsonb@disqus.com,Male,72.232.194.193,2015-12-24 12:19:28
+13,Mildred,Martinez,mmartinezc@samsung.com,Female,198.29.112.5,2015-12-24 12:19:28
+14,Dennis,Arnold,darnoldd@google.com,Male,86.96.3.250,2015-12-24 12:19:28
+15,Judy,Gray,jgraye@opensource.org,Female,79.218.162.245,2015-12-24 12:19:28
+16,Theresa,Garza,tgarzaf@epa.gov,Female,21.59.100.54,2015-12-24 12:19:28
+17,Gerald,Robertson,grobertsong@csmonitor.com,Male,131.134.82.96,2015-12-24 12:19:28
+18,Philip,Hernandez,phernandezh@adobe.com,Male,254.196.137.72,2015-12-24 12:19:28
+19,Julia,Gonzalez,jgonzalezi@cam.ac.uk,Female,84.240.227.174,2015-12-24 12:19:28
+20,Andrew,Davis,adavisj@patch.com,Male,9.255.67.25,2015-12-24 12:19:28
+21,Kimberly,Harper,kharperk@foxnews.com,Female,198.208.120.253,2015-12-24 12:19:28
+22,Mark,Martin,mmartinl@marketwatch.com,Male,233.138.182.153,2015-12-24 12:19:28
+23,Cynthia,Ruiz,cruizm@google.fr,Female,18.178.187.201,2015-12-24 12:19:28
+24,Samuel,Carroll,scarrolln@youtu.be,Male,128.113.96.122,2015-12-24 12:19:28
+25,Jennifer,Larson,jlarsono@vinaora.com,Female,98.234.85.95,2015-12-24 12:19:28
+26,Ashley,Perry,aperryp@rakuten.co.jp,Female,247.173.114.52,2015-12-24 12:19:28
+27,Howard,Rodriguez,hrodriguezq@shutterfly.com,Male,231.188.95.26,2015-12-24 12:19:28
+28,Amy,Brooks,abrooksr@theatlantic.com,Female,141.199.174.118,2015-12-24 12:19:28
+29,Louise,Warren,lwarrens@adobe.com,Female,96.105.158.28,2015-12-24 12:19:28
+30,Tina,Watson,twatsont@myspace.com,Female,251.142.118.177,2015-12-24 12:19:28
+31,Janice,Kelley,jkelleyu@creativecommons.org,Female,239.167.34.233,2015-12-24 12:19:28
+32,Terry,Mccoy,tmccoyv@bravesites.com,Male,117.201.183.203,2015-12-24 12:19:28
+33,Jeffrey,Morgan,jmorganw@surveymonkey.com,Male,78.101.78.149,2015-12-24 12:19:28
+34,Louis,Harvey,lharveyx@sina.com.cn,Male,51.50.0.167,2015-12-24 12:19:28
+35,Philip,Miller,pmillery@samsung.com,Male,103.255.222.110,2015-12-24 12:19:28
+36,Willie,Marshall,wmarshallz@ow.ly,Male,149.219.91.68,2015-12-24 12:19:28
+37,Patrick,Lopez,plopez10@redcross.org,Male,250.136.229.89,2015-12-24 12:19:28
+38,Adam,Jenkins,ajenkins11@harvard.edu,Male,7.36.112.81,2015-12-24 12:19:28
+39,Benjamin,Cruz,bcruz12@linkedin.com,Male,32.38.98.15,2015-12-24 12:19:28
+40,Ruby,Hawkins,rhawkins13@gmpg.org,Female,135.171.129.255,2015-12-24 12:19:28
+41,Carlos,Barnes,cbarnes14@a8.net,Male,240.197.85.140,2015-12-24 12:19:28
+42,Ruby,Griffin,rgriffin15@bravesites.com,Female,19.29.135.24,2015-12-24 12:19:28
+43,Sean,Mason,smason16@icq.com,Male,159.219.155.249,2015-12-24 12:19:28
+44,Anthony,Payne,apayne17@utexas.edu,Male,235.168.199.218,2015-12-24 12:19:28
+45,Steve,Cruz,scruz18@pcworld.com,Male,238.201.81.198,2015-12-24 12:19:28
+46,Anthony,Garcia,agarcia19@flavors.me,Male,25.85.10.18,2015-12-24 12:19:28
+47,Doris,Lopez,dlopez1a@sphinn.com,Female,245.218.51.238,2015-12-24 12:19:28
+48,Susan,Nichols,snichols1b@freewebs.com,Female,199.99.9.61,2015-12-24 12:19:28
+49,Wanda,Ferguson,wferguson1c@yahoo.co.jp,Female,236.241.135.21,2015-12-24 12:19:28
+50,Andrea,Pierce,apierce1d@google.co.uk,Female,132.40.10.209,2015-12-24 12:19:28
+51,Lawrence,Phillips,lphillips1e@jugem.jp,Male,72.226.82.87,2015-12-24 12:19:28
+52,Judy,Gilbert,jgilbert1f@multiply.com,Female,196.250.15.142,2015-12-24 12:19:28
+53,Eric,Williams,ewilliams1g@joomla.org,Male,222.202.73.126,2015-12-24 12:19:28
+54,Ralph,Romero,rromero1h@sogou.com,Male,123.184.125.212,2015-12-24 12:19:28
+55,Jean,Wilson,jwilson1i@ocn.ne.jp,Female,176.106.32.194,2015-12-24 12:19:28
+56,Lori,Reynolds,lreynolds1j@illinois.edu,Female,114.181.203.22,2015-12-24 12:19:28
+57,Donald,Moreno,dmoreno1k@bbc.co.uk,Male,233.249.97.60,2015-12-24 12:19:28
+58,Steven,Berry,sberry1l@eepurl.com,Male,186.193.50.50,2015-12-24 12:19:28
+59,Theresa,Shaw,tshaw1m@people.com.cn,Female,120.37.71.222,2015-12-24 12:19:28
+60,John,Stephens,jstephens1n@nationalgeographic.com,Male,191.87.127.115,2015-12-24 12:19:28
+61,Richard,Jacobs,rjacobs1o@state.tx.us,Male,66.210.83.155,2015-12-24 12:19:28
+62,Andrew,Lawson,alawson1p@over-blog.com,Male,54.98.36.94,2015-12-24 12:19:28
+63,Peter,Morgan,pmorgan1q@rambler.ru,Male,14.77.29.106,2015-12-24 12:19:28
+64,Nicole,Garrett,ngarrett1r@zimbio.com,Female,21.127.74.68,2015-12-24 12:19:28
+65,Joshua,Kim,jkim1s@edublogs.org,Male,57.255.207.41,2015-12-24 12:19:28
+66,Ralph,Roberts,rroberts1t@people.com.cn,Male,222.143.131.109,2015-12-24 12:19:28
+67,George,Montgomery,gmontgomery1u@smugmug.com,Male,76.75.111.77,2015-12-24 12:19:28
+68,Gerald,Alvarez,galvarez1v@flavors.me,Male,58.157.186.194,2015-12-24 12:19:28
+69,Donald,Olson,dolson1w@whitehouse.gov,Male,69.65.74.135,2015-12-24 12:19:28
+70,Carlos,Morgan,cmorgan1x@pbs.org,Male,96.20.140.87,2015-12-24 12:19:28
+71,Aaron,Stanley,astanley1y@webnode.com,Male,163.119.217.44,2015-12-24 12:19:28
+72,Virginia,Long,vlong1z@spiegel.de,Female,204.150.194.182,2015-12-24 12:19:28
+73,Robert,Berry,rberry20@tripadvisor.com,Male,104.19.48.241,2015-12-24 12:19:28
+74,Antonio,Brooks,abrooks21@unesco.org,Male,210.31.7.24,2015-12-24 12:19:28
+75,Ruby,Garcia,rgarcia22@ovh.net,Female,233.218.162.214,2015-12-24 12:19:28
+76,Jack,Hanson,jhanson23@blogtalkradio.com,Male,31.55.46.199,2015-12-24 12:19:28
+77,Kathryn,Nelson,knelson24@walmart.com,Female,14.189.146.41,2015-12-24 12:19:28
+78,Jason,Reed,jreed25@printfriendly.com,Male,141.189.89.255,2015-12-24 12:19:28
+79,George,Coleman,gcoleman26@people.com.cn,Male,81.189.221.144,2015-12-24 12:19:28
+80,Rose,King,rking27@ucoz.com,Female,212.123.168.231,2015-12-24 12:19:28
+81,Johnny,Holmes,jholmes28@boston.com,Male,177.3.93.188,2015-12-24 12:19:28
+82,Katherine,Gilbert,kgilbert29@altervista.org,Female,199.215.169.61,2015-12-24 12:19:28
+83,Joshua,Thomas,jthomas2a@ustream.tv,Male,0.8.205.30,2015-12-24 12:19:28
+84,Julie,Perry,jperry2b@opensource.org,Female,60.116.114.192,2015-12-24 12:19:28
+85,Richard,Perry,rperry2c@oracle.com,Male,181.125.70.232,2015-12-24 12:19:28
+86,Kenneth,Ruiz,kruiz2d@wikimedia.org,Male,189.105.137.109,2015-12-24 12:19:28
+87,Jose,Morgan,jmorgan2e@webnode.com,Male,101.134.215.156,2015-12-24 12:19:28
+88,Donald,Campbell,dcampbell2f@goo.ne.jp,Male,102.120.215.84,2015-12-24 12:19:28
+89,Debra,Collins,dcollins2g@uol.com.br,Female,90.13.153.235,2015-12-24 12:19:28
+90,Jesse,Johnson,jjohnson2h@stumbleupon.com,Male,225.178.125.53,2015-12-24 12:19:28
+91,Elizabeth,Stone,estone2i@histats.com,Female,123.184.126.221,2015-12-24 12:19:28
+92,Angela,Rogers,arogers2j@goodreads.com,Female,98.104.132.187,2015-12-24 12:19:28
+93,Emily,Dixon,edixon2k@mlb.com,Female,39.190.75.57,2015-12-24 12:19:28
+94,Albert,Scott,ascott2l@tinypic.com,Male,40.209.13.189,2015-12-24 12:19:28
+95,Barbara,Peterson,bpeterson2m@ow.ly,Female,75.249.136.180,2015-12-24 12:19:28
+96,Adam,Greene,agreene2n@fastcompany.com,Male,184.173.109.144,2015-12-24 12:19:28
+97,Earl,Sanders,esanders2o@hc360.com,Male,247.34.90.117,2015-12-24 12:19:28
+98,Angela,Brooks,abrooks2p@mtv.com,Female,10.63.249.126,2015-12-24 12:19:28
+99,Harold,Foster,hfoster2q@privacy.gov.au,Male,139.214.40.244,2015-12-24 12:19:28
+100,Carl,Meyer,cmeyer2r@disqus.com,Male,204.117.7.88,2015-12-24 12:19:28
diff --git a/tests/functional/graph_selection/test_graph_selection.py b/tests/functional/graph_selection/test_graph_selection.py
index d9b47fe9ac6..0e194913b17 100644
--- a/tests/functional/graph_selection/test_graph_selection.py
+++ b/tests/functional/graph_selection/test_graph_selection.py
@@ -2,8 +2,7 @@
 import json
 import pytest
 
-from dbt.tests.util import run_dbt
-from dbt.tests.tables import TableComparison
+from dbt.tests.util import run_dbt, check_relations_equal
 from tests.functional.graph_selection.fixtures import SelectionFixtures
 from dbt.context import providers
 from unittest.mock import patch
@@ -53,10 +52,7 @@ def test_specific_model(self, project):
         results = run_dbt(["run", "--select", "users"])
         assert len(results) == 1
 
-        table_comp = TableComparison(
-            adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-        )
-        table_comp.assert_tables_equal("seed", "users")
+        check_relations_equal(project.adapter, ["seed", "users"])
 
         created_tables = project.get_tables_in_schema()
         assert "users_rollup" not in created_tables
@@ -109,11 +105,8 @@ def test_tags_and_children_limited(self, project):
     def test_specific_model_and_children(self, project):
         results = run_dbt(["run", "--select", "users+"])
         assert len(results) == 4
-        table_comp = TableComparison(
-            adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-        )
-        table_comp.assert_tables_equal("seed", "users")
-        table_comp.assert_tables_equal("summary_expected", "users_rollup")
+        check_relations_equal(project.adapter, ["seed", "users"])
+        check_relations_equal(project.adapter, ["summary_expected", "users_rollup"])
 
         created_models = project.get_tables_in_schema()
         assert "emails_alt" in created_models
@@ -125,11 +118,8 @@ def test_specific_model_and_children(self, project):
     def test_specific_model_and_children_limited(self, project):
         results = run_dbt(["run", "--select", "users+1"])
         assert len(results) == 3
-        table_comp = TableComparison(
-            adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-        )
-        table_comp.assert_tables_equal("seed", "users")
-        table_comp.assert_tables_equal("summary_expected", "users_rollup")
+        check_relations_equal(project.adapter, ["seed", "users"])
+        check_relations_equal(project.adapter, ["summary_expected", "users_rollup"])
 
         created_models = project.get_tables_in_schema()
         assert "emails_alt" in created_models
@@ -141,11 +131,8 @@ def test_specific_model_and_children_limited(self, project):
     def test_specific_model_and_parents(self, project):
         results = run_dbt(["run", "--select", "+users_rollup"])
         assert len(results) == 2
-        table_comp = TableComparison(
-            adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-        )
-        table_comp.assert_tables_equal("seed", "users")
-        table_comp.assert_tables_equal("summary_expected", "users_rollup")
+        check_relations_equal(project.adapter, ["seed", "users"])
+        check_relations_equal(project.adapter, ["summary_expected", "users_rollup"])
 
         created_models = project.get_tables_in_schema()
         assert not ("base_users" in created_models)
@@ -155,11 +142,8 @@ def test_specific_model_and_parents(self, project):
     def test_specific_model_and_parents_limited(self, project):
         results = run_dbt(["run", "--select", "1+users_rollup"])
         assert len(results) == 2
-        table_comp = TableComparison(
-            adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-        )
-        table_comp.assert_tables_equal("seed", "users")
-        table_comp.assert_tables_equal("summary_expected", "users_rollup")
+        check_relations_equal(project.adapter, ["seed", "users"])
+        check_relations_equal(project.adapter, ["summary_expected", "users_rollup"])
 
         created_models = project.get_tables_in_schema()
         assert not ("base_users" in created_models)
@@ -172,10 +156,7 @@ def test_specific_model_with_exclusion(self, project):
         )
         assert len(results) == 1
 
-        table_comp = TableComparison(
-            adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-        )
-        table_comp.assert_tables_equal("seed", "users")
+        check_relations_equal(project.adapter, ["seed", "users"])
 
         created_models = project.get_tables_in_schema()
         assert not ("base_users" in created_models)
diff --git a/tests/functional/permission/test_permissions.py b/tests/functional/permission/test_permissions.py
index f4705935d17..390457f1b84 100644
--- a/tests/functional/permission/test_permissions.py
+++ b/tests/functional/permission/test_permissions.py
@@ -44,7 +44,7 @@ def profiles_config_update(self, unique_schema):
                         "user": "noaccess",
                         "pass": "password",
                         "dbname": os.getenv("POSTGRES_TEST_DATABASE", "dbt"),
-                        "schema": unique_schema + "_alt",  # Should this be the same unique_schema?
+                        "schema": unique_schema,
                     },
                 },
             }
@@ -64,7 +64,6 @@ def test_create_schema_permissions(
         project,
     ):
         # now it should work!
-        # breakpoint()
         project.run_sql("grant create on database {} to noaccess".format(project.database))
         project.run_sql(
             'grant usage, create on schema "{}" to noaccess'.format(project.test_schema)
@@ -72,5 +71,5 @@ def test_create_schema_permissions(
         project.run_sql(
             'grant select on all tables in schema "{}" to noaccess'.format(project.test_schema)
         )
-        results = run_dbt(["run", "--target", "noaccess"])
+        results = run_dbt(["run", "--target", "noaccess"], expect_pass=True)
         assert len(results) == 1
diff --git a/tests/functional/schema_tests/test_schema_v2_tests.py b/tests/functional/schema_tests/test_schema_v2_tests.py
index 3fa5a361da1..4f8c8a3afc6 100644
--- a/tests/functional/schema_tests/test_schema_v2_tests.py
+++ b/tests/functional/schema_tests/test_schema_v2_tests.py
@@ -58,7 +58,7 @@ def test_schema_tests(
     ):
         results = run_dbt()
         assert len(results) == 5
-        test_results = run_dbt(["test"])
+        test_results = run_dbt(["test"], expect_pass=False)
         # If the disabled model's tests ran, there would be 20 of these.
         assert len(test_results) == 19
 
@@ -140,7 +140,7 @@ def test_limit_schema_tests(
     ):
         results = run_dbt()
         assert len(results) == 3
-        test_results = run_dbt(["test"])
+        test_results = run_dbt(["test"], expect_pass=False)
         assert len(test_results) == 3
 
         for result in test_results:
@@ -186,7 +186,7 @@ def test_limit_schema_tests(
     ):
         results = run_dbt()
         assert len(results) == 3
-        test_results = run_dbt(["test"])
+        test_results = run_dbt(["test"], expect_pass=False)
         assert len(test_results) == 3
 
         for result in test_results:
@@ -239,7 +239,7 @@ def test_limit_schema_tests(
     ):
         results = run_dbt()
         assert len(results) == 3
-        test_results = run_dbt(["test"])
+        test_results = run_dbt(["test"], expect_pass=False)
         assert len(test_results) == 3
 
         for result in test_results:
@@ -425,7 +425,7 @@ def test_schema_tests(
         results = run_dbt()
         assert len(results) == 4
 
-        test_results = run_dbt(["test"])
+        test_results = run_dbt(["test"], expect_pass=False)
         assert len(test_results) == 6
 
         expected_failures = [
diff --git a/tests/functional/simple_snapshot/test_basic_snapshot.py b/tests/functional/simple_snapshot/test_basic_snapshot.py
index a8aded3d9d8..d6aec68ab6c 100644
--- a/tests/functional/simple_snapshot/test_basic_snapshot.py
+++ b/tests/functional/simple_snapshot/test_basic_snapshot.py
@@ -2,8 +2,7 @@
 from datetime import datetime
 import pytz
 import pytest
-from dbt.tests.util import run_dbt
-from dbt.tests.tables import TableComparison
+from dbt.tests.util import run_dbt, check_relations_equal, relation_from_name
 from tests.functional.simple_snapshot.fixtures import (
     models__schema_yml,
     models__ref_snapshot_sql,
@@ -61,57 +60,14 @@
 """
 
 
-class OverrideTableComparison_dbt(TableComparison):
-    def _assert_tables_equal_sql(self, relation_a, relation_b, columns=None):
-        # When building the equality tests, only test columns that don't start
-        # with 'dbt_', because those are time-sensitive
-        if columns is None:
-            columns = [
-                c
-                for c in self.get_relation_columns(relation_a)
-                if not c[0].lower().startswith("dbt_")
-            ]
-        return super()._assert_tables_equal_sql(relation_a, relation_b, columns=columns)
-
-
-class RevivedTableComparison(TableComparison):
-    def _assert_tables_equal_sql(self, relation_a, relation_b, columns=None):
-        revived_records = self.run_sql(
-            """
-            select
-                id,
-                updated_at,
-                dbt_valid_from
-            from {}
-            """.format(
-                relation_b
-            ),
-            fetch="all",
-        )
-
-        for result in revived_records:
-            # result is a tuple, the updated_at is second and dbt_valid_from is latest
-            assert isinstance(result[1], datetime)
-            assert isinstance(result[2], datetime)
-            assert result[1].replace(tzinfo=pytz.UTC) == result[2].replace(tzinfo=pytz.UTC)
-
-        if columns is None:
-            columns = [
-                c
-                for c in self.get_relation_columns(relation_a)
-                if not c[0].lower().startswith("dbt_")
-            ]
-        return super()._assert_tables_equal_sql(relation_a, relation_b, columns=columns)
-
-
-def snapshot_setup(project, NUM_SNAPSHOT_MODELS, table_comp):
+def snapshot_setup(project, num_snapshot_models=1):
     path = os.path.join(project.test_data_dir, "seed_pg.sql")
     project.run_sql_file(path)
     results = run_dbt(["snapshot"])
-    assert len(results) == NUM_SNAPSHOT_MODELS
+    assert len(results) == num_snapshot_models
 
     run_dbt(["test"])
-    table_comp.assert_tables_equal("snapshot_actual", "snapshot_expected")
+    check_relations_equal(project.adapter, ["snapshot_actual", "snapshot_expected"])
 
     path = os.path.join(project.test_data_dir, "invalidate_postgres.sql")
     project.run_sql_file(path)
@@ -120,60 +76,22 @@ def snapshot_setup(project, NUM_SNAPSHOT_MODELS, table_comp):
     project.run_sql_file(path)
 
     results = run_dbt(["snapshot"])
-    assert len(results) == NUM_SNAPSHOT_MODELS
+    assert len(results) == num_snapshot_models
 
     run_dbt(["test"])
-    table_comp.assert_tables_equal("snapshot_actual", "snapshot_expected")
+    check_relations_equal(project.adapter, ["snapshot_actual", "snapshot_expected"])
 
 
-def ref_setup(project, NUM_SNAPSHOT_MODELS):
+def ref_setup(project, num_snapshot_models=1):
     path = os.path.join(project.test_data_dir, "seed_pg.sql")
     project.run_sql_file(path)
     results = run_dbt(["snapshot"])
-    assert len(results) == NUM_SNAPSHOT_MODELS
+    assert len(results) == num_snapshot_models
 
     results = run_dbt(["run"])
     assert len(results) == 1
 
 
-# these fixtures are slight variations of each other for the basic snapshot tests run
-def basic_snapshot(project):
-    NUM_SNAPSHOT_MODELS = 1
-    table_comp = TableComparison(
-        adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-    )
-
-    snapshot_setup(project, NUM_SNAPSHOT_MODELS, table_comp)
-
-
-def check_cols_snapshot(project):
-    NUM_SNAPSHOT_MODELS = 2
-    table_comp = OverrideTableComparison_dbt(
-        adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-    )
-
-    snapshot_setup(project, NUM_SNAPSHOT_MODELS, table_comp)
-
-
-def revived_snapshot(project):
-    NUM_SNAPSHOT_MODELS = 2
-    table_comp = RevivedTableComparison(
-        adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-    )
-
-    snapshot_setup(project, NUM_SNAPSHOT_MODELS, table_comp)
-
-
-def basic_ref(project):
-    NUM_SNAPSHOT_MODELS = 1
-    ref_setup(project, NUM_SNAPSHOT_MODELS)
-
-
-def basic_ref_two_snapshots(project):
-    NUM_SNAPSHOT_MODELS = 2
-    ref_setup(project, NUM_SNAPSHOT_MODELS)
-
-
 class Basic:
     @pytest.fixture(scope="class")
     def snapshots(self):
@@ -197,12 +115,12 @@ def seeds(self):
 
 class TestBasicSnapshot(Basic):
     def test_basic_snapshot(self, project):
-        basic_snapshot(project)
+        snapshot_setup(project, num_snapshot_models=1)
 
 
 class TestBasicRef(Basic):
     def test_basic_ref(self, project):
-        basic_ref(project)
+        ref_setup(project, num_snapshot_models=1)
 
 
 class CustomNamespace:
@@ -231,12 +149,12 @@ def seeds(self):
 
 class TestBasicCustomNamespace(CustomNamespace):
     def test_custom_namespace_snapshot(self, project):
-        basic_snapshot(project)
+        snapshot_setup(project, num_snapshot_models=1)
 
 
 class TestRefCustomNamespace(CustomNamespace):
     def test_custom_namespace_ref(self, project):
-        basic_ref(project)
+        ref_setup(project, num_snapshot_models=1)
 
 
 class CustomSnapshot:
@@ -265,12 +183,12 @@ def seeds(self):
 
 class TestBasicCustomSnapshot(CustomSnapshot):
     def test_custom_snapshot(self, project):
-        basic_snapshot(project)
+        snapshot_setup(project, num_snapshot_models=1)
 
 
 class TestRefCustomSnapshot(CustomSnapshot):
     def test_custom_ref(self, project):
-        basic_ref(project)
+        ref_setup(project, num_snapshot_models=1)
 
 
 class CheckCols:
@@ -296,12 +214,12 @@ def seeds(self):
 
 class TestBasicCheckCols(CheckCols):
     def test_basic_snapshot(self, project):
-        check_cols_snapshot(project)
+        snapshot_setup(project, num_snapshot_models=2)
 
 
 class TestRefCheckCols(CheckCols):
     def test_check_cols_ref(self, project):
-        basic_ref_two_snapshots(project)
+        ref_setup(project, num_snapshot_models=2)
 
 
 class ConfiguredCheckCols:
@@ -341,12 +259,12 @@ def project_config_update(self):
 
 class TestBasicConfiguredCheckCols(ConfiguredCheckCols):
     def test_configured_snapshot(self, project):
-        check_cols_snapshot(project)
+        snapshot_setup(project, num_snapshot_models=2)
 
 
 class TestRefConfiguredCheckCols(ConfiguredCheckCols):
     def test_configured_ref(self, project):
-        basic_ref_two_snapshots(project)
+        ref_setup(project, num_snapshot_models=2)
 
 
 class UpdatedAtCheckCols:
@@ -387,9 +305,24 @@ def project_config_update(self):
 
 class TestBasicUpdatedAtCheckCols(UpdatedAtCheckCols):
     def test_updated_at_snapshot(self, project):
-        revived_snapshot(project)
+        snapshot_setup(project, num_snapshot_models=2)
+
+        snapshot_expected_relation = relation_from_name(project.adapter, "snapshot_expected")
+        revived_records = project.run_sql(
+            """
+            select id, updated_at, dbt_valid_from from {}
+            """.format(
+                snapshot_expected_relation
+            ),
+            fetch="all",
+        )
+        for result in revived_records:
+            # result is a tuple, the updated_at is second and dbt_valid_from is latest
+            assert isinstance(result[1], datetime)
+            assert isinstance(result[2], datetime)
+            assert result[1].replace(tzinfo=pytz.UTC) == result[2].replace(tzinfo=pytz.UTC)
 
 
 class TestRefUpdatedAtCheckCols(UpdatedAtCheckCols):
     def test_updated_at_ref(self, project):
-        basic_ref_two_snapshots(project)
+        ref_setup(project, num_snapshot_models=2)
diff --git a/tests/functional/simple_snapshot/test_hard_delete_snapshot.py b/tests/functional/simple_snapshot/test_hard_delete_snapshot.py
index aa9c59fe7f1..2d553f314db 100644
--- a/tests/functional/simple_snapshot/test_hard_delete_snapshot.py
+++ b/tests/functional/simple_snapshot/test_hard_delete_snapshot.py
@@ -2,8 +2,7 @@
 from datetime import datetime
 import pytz
 import pytest
-from dbt.tests.util import run_dbt
-from dbt.tests.tables import TableComparison
+from dbt.tests.util import run_dbt, check_relations_equal
 from tests.functional.simple_snapshot.fixtures import (
     models__schema_yml,
     models__ref_snapshot_sql,
@@ -49,11 +48,7 @@ def test_snapshot_hard_delete(project):
 
     datetime_snapshot()
 
-    table_comp = TableComparison(
-        adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-    )
-
-    table_comp.assert_tables_equal("snapshot_expected", "snapshot_actual")
+    check_relations_equal(project.adapter, ["snapshot_expected", "snapshot_actual"])
 
     invalidated_snapshot_datetime = None
     revived_snapshot_datetime = None
diff --git a/tests/functional/simple_snapshot/test_select_exclude_snapshot.py b/tests/functional/simple_snapshot/test_select_exclude_snapshot.py
index d254c2b964d..f763c838915 100644
--- a/tests/functional/simple_snapshot/test_select_exclude_snapshot.py
+++ b/tests/functional/simple_snapshot/test_select_exclude_snapshot.py
@@ -1,7 +1,6 @@
 import os
 import pytest
-from dbt.tests.util import run_dbt
-from dbt.tests.tables import TableComparison
+from dbt.tests.util import run_dbt, check_relations_equal, check_table_does_not_exist
 from tests.functional.simple_snapshot.fixtures import (
     seeds__seed_newcol_csv,
     seeds__seed_csv,
@@ -21,13 +20,10 @@ def all_snapshots(project):
     results = run_dbt(["snapshot"])
     assert len(results) == 4
 
-    table_comp = TableComparison(
-        adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-    )
-    table_comp.assert_tables_equal("snapshot_castillo", "snapshot_castillo_expected")
-    table_comp.assert_tables_equal("snapshot_alvarez", "snapshot_alvarez_expected")
-    table_comp.assert_tables_equal("snapshot_kelly", "snapshot_kelly_expected")
-    table_comp.assert_tables_equal("snapshot_actual", "snapshot_expected")
+    check_relations_equal(project.adapter, ["snapshot_castillo", "snapshot_castillo_expected"])
+    check_relations_equal(project.adapter, ["snapshot_alvarez", "snapshot_alvarez_expected"])
+    check_relations_equal(project.adapter, ["snapshot_kelly", "snapshot_kelly_expected"])
+    check_relations_equal(project.adapter, ["snapshot_actual", "snapshot_expected"])
 
     path = os.path.join(project.test_data_dir, "invalidate_postgres.sql")
     project.run_sql_file(path)
@@ -37,10 +33,10 @@ def all_snapshots(project):
 
     results = run_dbt(["snapshot"])
     assert len(results) == 4
-    table_comp.assert_tables_equal("snapshot_castillo", "snapshot_castillo_expected")
-    table_comp.assert_tables_equal("snapshot_alvarez", "snapshot_alvarez_expected")
-    table_comp.assert_tables_equal("snapshot_kelly", "snapshot_kelly_expected")
-    table_comp.assert_tables_equal("snapshot_actual", "snapshot_expected")
+    check_relations_equal(project.adapter, ["snapshot_castillo", "snapshot_castillo_expected"])
+    check_relations_equal(project.adapter, ["snapshot_alvarez", "snapshot_alvarez_expected"])
+    check_relations_equal(project.adapter, ["snapshot_kelly", "snapshot_kelly_expected"])
+    check_relations_equal(project.adapter, ["snapshot_actual", "snapshot_expected"])
 
 
 def exclude_snapshots(project):
@@ -49,13 +45,10 @@ def exclude_snapshots(project):
     results = run_dbt(["snapshot", "--exclude", "snapshot_castillo"])
     assert len(results) == 3
 
-    table_comp = TableComparison(
-        adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-    )
-    table_comp.assert_table_does_not_exist("snapshot_castillo")
-    table_comp.assert_tables_equal("snapshot_alvarez", "snapshot_alvarez_expected")
-    table_comp.assert_tables_equal("snapshot_kelly", "snapshot_kelly_expected")
-    table_comp.assert_tables_equal("snapshot_actual", "snapshot_expected")
+    check_table_does_not_exist(project.adapter, "snapshot_castillo")
+    check_relations_equal(project.adapter, ["snapshot_alvarez", "snapshot_alvarez_expected"])
+    check_relations_equal(project.adapter, ["snapshot_kelly", "snapshot_kelly_expected"])
+    check_relations_equal(project.adapter, ["snapshot_actual", "snapshot_expected"])
 
 
 def select_snapshots(project):
@@ -64,13 +57,10 @@ def select_snapshots(project):
     results = run_dbt(["snapshot", "--select", "snapshot_castillo"])
     assert len(results) == 1
 
-    table_comp = TableComparison(
-        adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-    )
-    table_comp.assert_tables_equal("snapshot_castillo", "snapshot_castillo_expected")
-    table_comp.assert_table_does_not_exist("snapshot_alvarez")
-    table_comp.assert_table_does_not_exist("snapshot_kelly")
-    table_comp.assert_table_does_not_exist("snapshot_actual")
+    check_relations_equal(project.adapter, ["snapshot_castillo", "snapshot_castillo_expected"])
+    check_table_does_not_exist(project.adapter, "snapshot_alvarez")
+    check_table_does_not_exist(project.adapter, "snapshot_kelly")
+    check_table_does_not_exist(project.adapter, "snapshot_actual")
 
 
 # all of the tests below use one of both of the above tests with
diff --git a/tests/functional/source_overrides/test_simple_source_override.py b/tests/functional/source_overrides/test_simple_source_override.py
index 749221dbfea..7ff5ef09cab 100644
--- a/tests/functional/source_overrides/test_simple_source_override.py
+++ b/tests/functional/source_overrides/test_simple_source_override.py
@@ -1,8 +1,7 @@
 from datetime import datetime, timedelta
 import pytest
 
-from dbt.tests.util import run_dbt, update_config_file
-from dbt.tests.tables import TableComparison
+from dbt.tests.util import run_dbt, update_config_file, check_relations_equal
 from dbt.tests.fixtures.project import write_project_files
 from tests.functional.source_overrides.fixtures import (  # noqa: F401
     local_dependency,
@@ -106,10 +105,7 @@ def test_source_overrides(self, project):
         results = run_dbt(["run"])
         assert len(results) == 1
 
-        table_comp = TableComparison(
-            adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-        )
-        table_comp.assert_tables_equal("expected_result", "my_model")
+        check_relations_equal(project.adapter, ["expected_result", "my_model"])
 
         # set the updated_at field of this seed to last week
         insert_id = self._set_updated_at_to(insert_id, timedelta(days=-7), project)
diff --git a/tests/functional/sources/test_simple_source.py b/tests/functional/sources/test_simple_source.py
index c59ce2a185b..53c3f1868f1 100644
--- a/tests/functional/sources/test_simple_source.py
+++ b/tests/functional/sources/test_simple_source.py
@@ -3,8 +3,12 @@
 import yaml
 from dbt.exceptions import ParsingException
 
-from dbt.tests.util import run_dbt, update_config_file
-from dbt.tests.tables import TableComparison
+from dbt.tests.util import (
+    run_dbt,
+    update_config_file,
+    check_relations_equal,
+    check_table_does_not_exist,
+)
 from tests.functional.sources.common_source_setup import (
     BaseSourcesTest,
 )
@@ -63,16 +67,13 @@ class TestBasicSource(SuccessfulSourcesTest):
     def test_basic_source_def(self, project):
         results = self.run_dbt_with_vars(project, ["run"])
         assert len(results) == 4
-        table_comp = TableComparison(
-            adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-        )
-        table_comp.assert_many_tables_equal(
-            ["source", "descendant_model", "nonsource_descendant"],
-            ["expected_multi_source", "multi_source_model"],
+
+        check_relations_equal(
+            project.adapter, ["source", "descendant_model", "nonsource_descendant"]
         )
+        check_relations_equal(project.adapter, ["expected_multi_source", "multi_source_model"])
         results = self.run_dbt_with_vars(project, ["test"])
         assert len(results) == 6
-        print(results)
 
 
 class TestSourceSelector(SuccessfulSourcesTest):
@@ -82,12 +83,9 @@ def test_source_selector(self, project):
             project, ["run", "--models", "source:test_source.test_table+"]
         )
         assert len(results) == 1
-        table_comp = TableComparison(
-            adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-        )
-        table_comp.assert_tables_equal("source", "descendant_model")
-        table_comp.assert_table_does_not_exist("nonsource_descendant")
-        table_comp.assert_table_does_not_exist("multi_source_model")
+        check_relations_equal(project.adapter, ["source", "descendant_model"])
+        check_table_does_not_exist(project.adapter, "nonsource_descendant")
+        check_table_does_not_exist(project.adapter, "multi_source_model")
 
         # do the same thing, but with tags
         results = self.run_dbt_with_vars(
@@ -120,12 +118,9 @@ def test_empty_source_def(self, project):
         results = self.run_dbt_with_vars(
             project, ["run", "--models", "source:test_source.test_table"]
         )
-        table_comp = TableComparison(
-            adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-        )
-        table_comp.assert_table_does_not_exist("nonsource_descendant")
-        table_comp.assert_table_does_not_exist("multi_source_model")
-        table_comp.assert_table_does_not_exist("descendant_model")
+        check_table_does_not_exist(project.adapter, "nonsource_descendant")
+        check_table_does_not_exist(project.adapter, "multi_source_model")
+        check_table_does_not_exist(project.adapter, "descendant_model")
         assert len(results) == 0
 
 
@@ -133,33 +128,24 @@ class TestSourceDef(SuccessfulSourcesTest):
     def test_source_only_def(self, project):
         results = self.run_dbt_with_vars(project, ["run", "--models", "source:other_source+"])
         assert len(results) == 1
-        table_comp = TableComparison(
-            adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-        )
-        table_comp.assert_tables_equal("expected_multi_source", "multi_source_model")
-        table_comp.assert_table_does_not_exist("nonsource_descendant")
-        table_comp.assert_table_does_not_exist("descendant_model")
+        check_relations_equal(project.adapter, ["expected_multi_source", "multi_source_model"])
+        check_table_does_not_exist(project.adapter, "nonsource_descendant")
+        check_table_does_not_exist(project.adapter, "descendant_model")
 
         results = self.run_dbt_with_vars(project, ["run", "--models", "source:test_source+"])
         assert len(results) == 2
-        table_comp.assert_many_tables_equal(
-            ["source", "descendant_model"], ["expected_multi_source", "multi_source_model"]
-        )
-        table_comp.assert_table_does_not_exist("nonsource_descendant")
+        check_relations_equal(project.adapter, ["source", "descendant_model"])
+        check_relations_equal(project.adapter, ["expected_multi_source", "multi_source_model"])
+        check_table_does_not_exist(project.adapter, "nonsource_descendant")
 
 
 class TestSourceChildrenParents(SuccessfulSourcesTest):
     def test_source_childrens_parents(self, project):
         results = self.run_dbt_with_vars(project, ["run", "--models", "@source:test_source"])
         assert len(results) == 2
-        table_comp = TableComparison(
-            adapter=project.adapter, unique_schema=project.test_schema, database=project.database
-        )
-        table_comp.assert_many_tables_equal(
-            ["source", "descendant_model"],
-            ["expected_multi_source", "multi_source_model"],
-        )
-        table_comp.assert_table_does_not_exist("nonsource_descendant")
+        check_relations_equal(project.adapter, ["source", "descendant_model"])
+        check_relations_equal(project.adapter, ["expected_multi_source", "multi_source_model"])
+        check_table_does_not_exist(project.adapter, "nonsource_descendant")
 
 
 class TestSourceRunOperation(SuccessfulSourcesTest):
diff --git a/tests/functional/sources/test_source_freshness.py b/tests/functional/sources/test_source_freshness.py
index c2a23de5adc..4bbcd85706a 100644
--- a/tests/functional/sources/test_source_freshness.py
+++ b/tests/functional/sources/test_source_freshness.py
@@ -292,7 +292,7 @@ def test_source_freshness_error(self, project):
 
 class TestSourceFreshnessFilter(SuccessfulSourceFreshnessTest):
     @pytest.fixture(scope="class")
-    def filtered_models(self):
+    def models(self):
         return {"schema.yml": filtered_models__schema_yml}
 
     def test_source_freshness_all_records(self, project):


Code review comments:
diff_hunk: @@ -311,17 +375,47 @@ def generate_update_clause(adapter, clause) -> str:
             raise TestProcessingException("Invalid update_rows clause: no src_col")
         add_to = clause["src_col"]
         kwargs = {k: v for k, v in clause.items() if k in ("interval", "number")}
-        with patch.object(providers, "get_adapter", return_value=adapter):
-            with adapter.connection_named("_test"):
-                return adapter.timestamp_add_sql(add_to=add_to, **kwargs)
+        with get_connection(adapter):
+            return adapter.timestamp_add_sql(add_to=add_to, **kwargs)
     elif clause_type == "add_string":
         for key in ["src_col", "value"]:
             if key not in clause:
                 raise TestProcessingException(f"Invalid update_rows clause: no {key}")
         src_col = clause["src_col"]
         value = clause["value"]
         location = clause.get("location", "append")
-        with patch.object(providers, "get_adapter", return_value=adapter):
-            with adapter.connection_named("_test"):
-                return adapter.string_add_sql(src_col, value, location)
+        with get_connection(adapter):
+            return adapter.string_add_sql(src_col, value, location)
     return ""
+
+
+@contextmanager
+def get_connection(adapter, name="_test"):
+    # Since the 'adapter' in dbt.adapters.factory may have been replaced by execution
+    # of dbt commands since the test 'adapter' was created, we patch the 'get_adapter' call in
+    # dbt.context.providers, so that macros that are called refer to this test adapter.
+    # This allows tests to run normal adapter macros as if reset_adapters() were not
+    # called by handle_and_check (for asserts, etc).
+    with patch.object(providers, "get_adapter", return_value=adapter):
+        with adapter.connection_named(name):
+            conn = adapter.connections.get_thread_connection()
+            yield conn
path: core/dbt/tests/util.py
text: @user1:
This still feels a bit unpleasant, but I'm happy to see that unpleasantness more narrowly contained within this one method 

@author:
I wonder if there's a way to use the current adapter instead.  This method was copied from test/integration/base.py. It would be nice to not have to do this.

@author:
Yay! Was able to take off the patch bit.

@author:
I removed a permission test after this change because it was breaking. The test was flaky to start with and really wasn't doing the right thing anyway. I figured that we have lots of tests that show that users with the right permissions work :) but if you think we still need something similar, we should make a separate ticket and do it right (creating randomized user name, no permissions, test, change permissions, test, drop user and permissions). 

@author:
Oh, just fyi, the issue was that after running the permission SQL in the dropped test, the "drop schema" in the project fixture was failing because it didn't have the right permissions.