Repository: DistrictDataLabs/yellowbrick

Issue:
Overhaul unittest and fixtures
Remove dependency on `unittest.TestCase` by replacing assertions with `assert` statements. This also involves using `pytest.raises` and `pytest.skip` decorators. 

Use PyTest fixtures for datasets and other fixtures. 

~Add PEP checking to pytest (alongside pyflakes)~

- [x] rename `ThingTests` to `TestThing` 
- [x] assertions 
- [x] pytest.skip 
- [x] dataset fixtures 
- [ ] ~PEP8~

Alongside this we should also do a PY2 removal check, namely anywhere:

```python
try:
    from unittest import mock
except ImportError:
    import mock
```

should be replaced with just `from unittest import mock` 


Discussion:
@bbengfort what's the motivation for removing the unittest dependency?
@NealHumphrey the short answer is that weird things happen when you mix and match pytest and unittest - my primary concern is test discovery. PyTest expects `TestThing` vs `ThingTests` (although I guess that is more of a nose thing than a unittest thing). Plus pragmas and marks (e.g. skip, xfail) behave slightly differently. I think it's better to standardize on one or the other. 

Because our feeling is that people are more comfortable with pytest and because of features like pytest-flakes, pytest-spec, etc. we've chosen to move to pytest. Pytest does play well with unittest so there is no rush on this, but the sooner it's standard the easier it will be to diagnose issues in the tests (of which we have a few). 
Yeah @NealHumphrey, this is sort of a follow on from #291 -- after we discovered last year that [nose wasn't being maintained anymore](https://twitter.com/llanga/status/826144101833732097) and started converting all the tests to pytest.
Adding a TODO:
- [x] Update the [documentation](http://www.scikit-yb.org/en/latest/contributing.html#testing) for contributors
- [x] add pytest to requirements.txt
As noted in my pull request, I had trouble running `pytest` locally including pyflakes and coverage. On Windows, haven't tried it on Mac yet. 

What I tried:
1) Create a new virtual environment (using conda) 
2) activate it `activate yb`
3) pip install pytest (b/c it's not in the requirments.txt, see TODO in previous comment), which installed 3.4.2 plus some dependencies, and `pip install --upgrade pyflakes`  (installed v 1.6.0)
4) in root `yellowbrick` folder (i.e. not in `yellowbrick/tests` and not in `yellowbrick/yellowbrick`), run `python -m pytest`

Output: 
```
usage: pytest.py [options] [file_or_dir] [file_or_dir] [...]
pytest.py: error: unrecognized arguments: --cov=yellowbrick --flakes --spec
  inifile: C:\Users\humph\Documents\Github\yellowbrick\setup.cfg
  rootdir: C:\Users\humph\Documents\Github\yellowbrick
```

If I run without the virtual environment and use `python -m pytest`, it runs all the test but lots of them fail (44). There are various causes, though a lot of them seem to trace back to this:
```
    def _actual_img_path(self, extension='.png'):
        """Determines the correct outpath for drawing a matplotlib image that
            corresponds to the unittest module path.
            """
        module_path, test_func_name = self._setup_imagetest()
>       module_path = os.path.join(*module_path)
E       TypeError: join() missing 1 required positional argument: 'path'
```
@bbengfort see above
@NealHumphrey, thank you for the detailed notes. This is a very good point; the documentation has been updated with pytest, but is currently in develop and will be moved to latest once we go to 0.6 (very shortly). If you could review the develop documents we can try to adapt them to make it a bit friendlier. 

In [Forking the Repository](http://www.scikit-yb.org/en/develop/contributing.html#forking-the-repository) in the contributors guide, step 3 mentions `pip install -r tests/requirements.txt` and `pip install -r docs/requirements.txt`, this isn't in the testing section though which simply mentions:

> The Makefile uses the pytest runner and testing suite as well as the coverage library, so make sure you have those dependencies installed! The DatasetMixin also requires requests.py to fetch data from our Amazon S3 account.

We could adapt this section to reinforce installing the dependencies with `pip install -r tests/requirments.txt` and also mention using `pytest` directly rather than `make test`. 

I believe the error you are observing will be solved when you install pyspec and coverage using that requirements file. 

A note on the various requirements files: the main `requirements.txt` file is used in `setup.py` to determine dependencies, as such we have only the dependencies required for using yellowbrick as a library in it (nor do we manage our dependencies' subdependencies). The requirements for testing and documentation are actually in that file, just commented out and they're uncommented in the respective docs and test directories. 
@bbengfort potentially we could close this in favor of #456 and #682?
@rebeccabilbro thanks for grooming the backlog of issues! Although #456 and #682 are closely related to this issue, I think this is an independent task from those and there are several gotchas that this issue records (e.g. TestThing vs ThingTests). The good news is that in the PRs since this issue we've added no new unittest dependencies and have replaced some of the old dependencies with PyTest. I think that this should be (relatively) easily sorted when we do the v1.0 release. 
Gotcha @bbengfort, I’ll add to the v1.0 milestone in that case.
Recommend that we remove PEP8 from this as we are going to add Black in another V1.0 issue:
https://github.com/DistrictDataLabs/yellowbrick/issues/456

Resolving patch:
diff --git a/yellowbrick/features/rfecv.py b/yellowbrick/features/rfecv.py
--- a/yellowbrick/features/rfecv.py
+++ b/yellowbrick/features/rfecv.py
@@ -171,7 +171,7 @@ def fit(self, X, y=None):
         else:
             step = int(self.step)
 
-        if step < 0:
+        if step <= 0:
             raise YellowbrickValueError("step must be >0")
 
         # Create the RFE model


Test patch:
diff --git a/tests/README.md b/tests/README.md
--- a/tests/README.md
+++ b/tests/README.md
@@ -2,7 +2,7 @@
 
 *Welcome to the Yellowbrick tests!*
 
-If you're looking for information about how to use Yellowbrick, for our contributor's guide, for examples and teaching resources, for answers to frequently asked questions, and more, please visit the latest version of our documentation at [www.scikit-yb.org](https://www.scikit-yb.org/). 
+If you're looking for information about how to use Yellowbrick, for our contributor's guide, for examples and teaching resources, for answers to frequently asked questions, and more, please visit the latest version of our documentation at [www.scikit-yb.org](https://www.scikit-yb.org/).
 
 ## Running Yellowbrick Tests
 
@@ -20,7 +20,7 @@ Tests can then be run as follows from the project `root`:
 $ make test
 ```
 
-The Makefile uses the `pytest` runner and testing suite as well as the coverage library. 
+The Makefile uses the `pytest` runner and testing suite as well as the coverage library.
 
 ## Adding a Test for Your Visualizer
 
@@ -28,11 +28,11 @@ The `tests` package mirrors the yellowbrick package in structure and also contai
 
 ### Visual Tests
 
-The primary test you should create is simply to test your visualizer from end to end and make sure that no exceptions occur. 
+The primary test you should create is simply to test your visualizer from end to end and make sure that no exceptions occur.
 
-Visual tests are notoriously difficult to create --- how do you test a visualization or figure? Moreover, testing scikit-learn models with real data can consume a lot of memory. To assist with this, we have two primary helpers, `VisualTestCase` and the `yellowbrick.datasets` module. 
+Visual tests are notoriously difficult to create --- how do you test a visualization or figure? Moreover, testing scikit-learn models with real data can consume a lot of memory. To assist with this, we have two primary helpers, `VisualTestCase` and the `yellowbrick.datasets` module.
 
-Leverage these helpers to create your unittest as follows:
+Leverage these helpers to create your tests as follows:
 
 ```python
 import pytest
@@ -64,7 +64,7 @@ Writing an image-based comparison test is only a little more difficult than the
 
 The main consideration is that you must specify the “baseline” (i.e. expected) image in the `tests/baseline_images/` folder structure.
 
-For example, let's say you create your unittest in `tests/test_regressor/test_myvisualizer.py` as follows:
+For example, let's say you create your tests in `tests/test_regressor/test_myvisualizer.py` as follows:
 
 ```python
 from tests.base import VisualTestCase
diff --git a/tests/__init__.py b/tests/__init__.py
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -17,7 +17,6 @@
 ## Imports
 ##########################################################################
 
-import unittest
 import matplotlib
 
 ## IMPORTANT! Set matplotlib to use the Agg backend before imported anywhere!
@@ -35,13 +34,13 @@
 ## Initialization Tests
 ##########################################################################
 
-class InitializationTests(unittest.TestCase):
+class TestInitialization(object):
 
     def test_sanity(self):
         """
         Test that tests work by confirming 7-3 = 4
         """
-        self.assertEqual(7-3, 4, "The world went wrong!!")
+        assert 7 - 3 == 4, "The world went wrong!!"
 
     def test_import(self):
         """
@@ -58,6 +57,6 @@ def test_version(self):
         """
         try:
             import yellowbrick as yb
-            self.assertEqual(yb.__version__, EXPECTED_VERSION)
+            assert yb.__version__ == EXPECTED_VERSION
         except ImportError:
             self.fail("Could not import the yellowbrick library!")
diff --git a/tests/base.py b/tests/base.py
--- a/tests/base.py
+++ b/tests/base.py
@@ -15,24 +15,31 @@
 ##########################################################################
 
 import os
-import inspect
 import sys
+import inspect
 
-import unittest
 import matplotlib as mpl
 import matplotlib.pyplot as plt
 
 from matplotlib import ticker
-from matplotlib import rcParams
-
 from matplotlib.testing.compare import compare_images
 from yellowbrick.exceptions import ImageComparisonFailure
 
+
+##########################################################################
+## Environment
+##########################################################################
+
 def is_windows_or_conda():
+    """
+    Simple detection mechanism to determine if the tests are running in a
+    win32 or Anaconda/Miniconda environment.
+    """
     is_windows = sys.platform == 'win32'
     is_conda = os.path.exists(os.path.join(sys.prefix, 'conda-meta'))
     return is_windows or is_conda
 
+
 ##########################################################################
 ## Module Constants
 ##########################################################################
@@ -43,45 +50,45 @@ def is_windows_or_conda():
 BASELINE_IMAGES = os.path.join(TESTS, "baseline_images")
 IS_WINDOWS_OR_CONDA = is_windows_or_conda()
 
+
 ##########################################################################
 ## Visual Test Case
 ##########################################################################
 
-class VisualTestCase(unittest.TestCase):
-
-    @classmethod
-    def setUpClass(klass):
-        """
-        This setup function is available to ensure that all CI tests
-        that do visual work are set up correctly.
+class VisualTestCase(object):
+    """
+    The visual test case class ensures that all tests inside of the class
+    can execute image similarity tests inside of a clean matplotlib global
+    figure.
+    """
 
-        Note:
+    def setup_method(self):
         """
-        super(VisualTestCase, klass).setUpClass()
+        Before a visual test case method is run, ensure that the previous
+        figure is closed and the current axes are cleared.
 
-    def setUp(self):
-        """
-        Close all previous plots
+        See: https://docs.pytest.org/en/latest/xunit_setup.html
         """
         # Reset the matplotlib environment
-        plt.cla()        # clear current axis
-        plt.clf()        # clear current figure
-        plt.close("all") # close all existing plots
+        plt.cla()         # clear current axis
+        plt.clf()         # clear current figure
+        plt.close("all")  # close all existing plots
 
-        # Travis-CI does not have san-serif
-        rcParams['font.family'] = 'DejaVu Sans'
+        # Travis-CI does not have san-serif so ensure standard fonts are used.
+        # Note that this must be set before each test otherwise it will be reset by
+        # the Yellowbrick styles.
+        mpl.rcParams['font.family'] = 'DejaVu Sans'
 
-        super(VisualTestCase, self).setUp()
-
-    def assert_images_similar(self, visualizer=None, ax=None, tol=0.01, windows_tol=None,  **kwargs):
+    def assert_images_similar(self, visualizer=None, ax=None,
+                              tol=0.01, windows_tol=None, **kwargs):
         """Accessible testing method for testing generation of a Visualizer.
 
         Requires the placement of a baseline image for comparison in the
         tests/baseline_images folder that corresponds to the module path of the
         VisualTestCase being evaluated. The name of the image corresponds to
-        the unittest function where "self.assert_images_similar" is called.
+        the test function where "self.assert_images_similar" is called.
 
-        For example, calling "assert_images_similar" in the unittest
+        For example, calling "assert_images_similar" in the test function
         "test_class_report" in tests.test_classifier.test_class_balance would
         require placement a baseline image at:
 
@@ -93,20 +100,21 @@ def assert_images_similar(self, visualizer=None, ax=None, tol=0.01, windows_tol=
 
         actual_images/
 
-        visualizer : yellowbrick visualizer
+        visualizer : yellowbrick visualizer, default: None
             An instantiated yellowbrick visualizer that has been fitted,
             transformed and had all operations except for poof called on it.
 
         ax : matplotlib Axes, default: None
             The axis to plot the figure on.
 
-        tol : float
+        tol : float, default: 0.01
             The tolerance (a color value difference, where 255 is the
             maximal difference).  The test fails if the average pixel
             difference is greater than this value.
 
         windows_tol: float, default: None
-            Similar to the tol parameter, but targeted for testing on a windows environment. 
+            Similar to the tol parameter, but targeted for testing on a
+            windows environment.
 
         kwargs : dict
             Options to pass to the ImageComparison class.
@@ -116,7 +124,12 @@ def assert_images_similar(self, visualizer=None, ax=None, tol=0.01, windows_tol=
 
         # Build and execute the image comparison
         compare = ImageComparison(
-            inspect.stack(), visualizer=visualizer, ax=ax, tol=tol, windows_tol=windows_tol, **kwargs
+            inspect.stack(),
+            visualizer=visualizer,
+            ax=ax,
+            tol=tol,
+            windows_tol=windows_tol,
+            **kwargs
         )
         compare()
 
@@ -182,8 +195,8 @@ class ImageComparison(object):
     ValueError : at least one of visualizer or ax must be specified.
     """
 
-    def __init__(self, stack, visualizer=None, ax=None, tol=0.01, 
-                 windows_tol=0.01, ext=".png", remove_ticks=True, 
+    def __init__(self, stack, visualizer=None, ax=None, tol=0.01,
+                 windows_tol=0.01, ext=".png", remove_ticks=True,
                  remove_title=True, remove_labels=True, remove_legend=True):
 
         # Ensure we have something to draw on
@@ -216,10 +229,9 @@ def __init__(self, stack, visualizer=None, ax=None, tol=0.01,
         # Set the error tolerance depending on the os
         if os.name == "nt" and windows_tol is not None:
             self.tol = windows_tol
-        else:        
+        else:
             self.tol = tol
 
-
         # Save other image comparison properties
         self.ext = ext
         self.remove_ticks = remove_ticks
diff --git a/tests/conftest.py b/tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -18,9 +18,34 @@
 ##########################################################################
 
 import os
+import matplotlib as mpl
 
 from pytest_flakes import FlakesItem
 
+
+##########################################################################
+## Configure tests
+##########################################################################
+
+def pytest_configure(config):
+    """
+    This function is called by pytest for every plugin and conftest file
+    after the command line arguments have been passed but before the
+    session object is created and all of the tests are created. It is used
+    to set a global configuration before all tests are run.
+
+    Yellowbrick uses this function primarily to ensure that the matplotlib
+    environment is setup correctly for all tests.
+    """
+    # This is redundant with the line in tests/__init__.py but ensures that
+    # the backend is correctly set across all tests and plugins.
+    mpl.use('Agg')
+
+    # Travis-CI does not have san-serif so ensure standard fonts are used.
+    # TODO: this is currently being reset before each test; needs fixing.
+    mpl.rcParams['font.family'] = 'DejaVu Sans'
+
+
 ##########################################################################
 ## PyTest Hooks
 ##########################################################################
diff --git a/tests/dataset.py b/tests/dataset.py
--- a/tests/dataset.py
+++ b/tests/dataset.py
@@ -105,7 +105,7 @@
 
 class DatasetMixin(object):
     """
-    Mixin for unittest.TestCase class to download datasets from S3 for
+    Mixin for VisualTestCase class to download datasets from S3 for
     testing real world machine learning visual diagnostics.
     """
 
diff --git a/tests/requirements.txt b/tests/requirements.txt
--- a/tests/requirements.txt
+++ b/tests/requirements.txt
@@ -15,7 +15,7 @@ requests>=2.18.3
 
 # Optional Testing Dependencies
 nltk>=3.2
-# spacy>=2.0.18 
+# spacy>=2.0.18
 pandas>=0.20
 umap-learn==0.3
 numba==0.42
diff --git a/tests/test_base.py b/tests/test_base.py
--- a/tests/test_base.py
+++ b/tests/test_base.py
@@ -31,6 +31,7 @@
 
 from sklearn.datasets import make_classification
 
+
 ##########################################################################
 ## Base Cases
 ##########################################################################
diff --git a/tests/test_bestfit.py b/tests/test_bestfit.py
--- a/tests/test_bestfit.py
+++ b/tests/test_bestfit.py
@@ -35,49 +35,49 @@
 ## Best fit tests
 ##########################################################################
 
-class BestFitTests(VisualTestCase):
+class TestBestFit(VisualTestCase):
 
     def test_bad_estimator(self):
         """
         Test that a bad estimator name raises a value error.
         """
-        fig, axe = plt.subplots()
+        fig, ax = plt.subplots()
         X, y = ANSCOMBE[1]
 
-        with self.assertRaises(YellowbrickValueError):
-            draw_best_fit(X, y, axe, 'pepper')
+        with pytest.raises(YellowbrickValueError):
+            draw_best_fit(X, y, ax, 'pepper')
 
     def test_ensure_same_length(self):
         """
         Ensure that vectors of different lengths raise
         """
-        fig, axe = plt.subplots()
+        fig, ax = plt.subplots()
         X = np.array([1, 2, 3, 5, 8, 10, 2])
         y = np.array([1, 3, 6, 2])
 
-        with self.assertRaises(YellowbrickValueError):
-            draw_best_fit(X, y, axe, 'linear')
+        with pytest.raises(YellowbrickValueError):
+            draw_best_fit(X, y, ax, 'linear')
 
-        with self.assertRaises(YellowbrickValueError):
-            draw_best_fit(X[:,np.newaxis], y, axe, 'linear')
+        with pytest.raises(YellowbrickValueError):
+            draw_best_fit(X[:,np.newaxis], y, ax, 'linear')
 
     @pytest.mark.filterwarnings('ignore')
-    def testdraw_best_fit(self):
+    def test_draw_best_fit(self):
         """
         Test that drawing a best fit line works.
         """
-        fig, axe = plt.subplots()
+        fig, ax = plt.subplots()
         X, y = ANSCOMBE[0]
 
-        self.assertEqual(axe, draw_best_fit(X, y, axe, 'linear'))
-        self.assertEqual(axe, draw_best_fit(X, y, axe, 'quadratic'))
+        assert ax == draw_best_fit(X, y, ax, 'linear')
+        assert ax == draw_best_fit(X, y, ax, 'quadratic')
 
 
 ##########################################################################
 ## Estimator tests
 ##########################################################################
 
-class EstimatorTests(VisualTestCase):
+class TestEstimator(VisualTestCase):
     """
     Test the estimator functions for best fit lines.
     """
@@ -92,9 +92,8 @@ def test_linear(self):
         X = X[:,np.newaxis]
 
         model = fit_linear(X, y)
-        self.assertIsNotNone(model)
-        self.assertIsInstance(model, LinearRegression)
-
+        assert model is not None
+        assert isinstance(model, LinearRegression)
 
     def test_quadratic(self):
         """
@@ -106,8 +105,8 @@ def test_quadratic(self):
         X = X[:,np.newaxis]
 
         model = fit_quadratic(X, y)
-        self.assertIsNotNone(model)
-        self.assertIsInstance(model, Pipeline)
+        assert model is not None
+        assert isinstance(model, Pipeline)
 
     def test_select_best(self):
         """
@@ -119,8 +118,8 @@ def test_select_best(self):
         X = X[:,np.newaxis]
 
         model = fit_select_best(X, y)
-        self.assertIsNotNone(model)
-        self.assertIsInstance(model, Pipeline)
+        assert model is not None
+        assert isinstance(model, Pipeline)
 
         X, y = ANSCOMBE[3]
         X = np.array(X)
@@ -128,5 +127,5 @@ def test_select_best(self):
         X = X[:,np.newaxis]
 
         model = fit_select_best(X, y)
-        self.assertIsNotNone(model)
-        self.assertIsInstance(model, LinearRegression)
+        assert model is not None
+        assert isinstance(model, LinearRegression)
diff --git a/tests/test_classifier/__init__.py b/tests/test_classifier/__init__.py
--- a/tests/test_classifier/__init__.py
+++ b/tests/test_classifier/__init__.py
@@ -1,6 +1,13 @@
-#Backend must be set before first use. 
-# Setting backend here allows us to run tests just in this folder, without running the whole yellowbrick.tests folder
-# This command will have no effect if backend has already been set previously. 
-import matplotlib
-matplotlib.use('Agg')
+# tests.test_classifier
+# Tests for the classifier visualizers
+#
+# ID: __init__.py [] benjamin@bengfort.com $
+
+"""
+Tests for the classifier visualizers
+"""
+
+##########################################################################
+## Imports
+##########################################################################
 
diff --git a/tests/test_classifier/test_class_prediction_error.py b/tests/test_classifier/test_class_prediction_error.py
--- a/tests/test_classifier/test_class_prediction_error.py
+++ b/tests/test_classifier/test_class_prediction_error.py
@@ -45,7 +45,7 @@
 ##########################################################################
 
 
-class ClassPredictionErrorTests(VisualTestCase, DatasetMixin):
+class TestClassPredictionError(VisualTestCase, DatasetMixin):
 
     def test_integration_class_prediction_error(self):
         """
@@ -79,7 +79,7 @@ def test_classes_greater_than_indices(self):
         """
         model = LinearSVC()
         model.fit(X, y)
-        with self.assertRaises(ModelError):
+        with pytest.raises(ModelError):
             visualizer = ClassPredictionError(
                 model, classes=["A", "B", "C", "D", "E"]
             )
@@ -91,7 +91,7 @@ def test_classes_less_than_indices(self):
         """
         model = LinearSVC()
         model.fit(X, y)
-        with self.assertRaises(NotImplementedError):
+        with pytest.raises(NotImplementedError):
             visualizer = ClassPredictionError(model, classes=["A"])
             visualizer.score(X, y)
 
@@ -109,7 +109,7 @@ def test_class_type(self):
         X, y = make_multilabel_classification()
         model = RandomForestClassifier()
         model.fit(X, y)
-        with self.assertRaises(YellowbrickValueError):
+        with pytest.raises(YellowbrickValueError):
             visualizer = ClassPredictionError(model)
             visualizer.score(X, y)
 
diff --git a/tests/test_classifier/test_classification_report.py b/tests/test_classifier/test_classification_report.py
--- a/tests/test_classifier/test_classification_report.py
+++ b/tests/test_classifier/test_classification_report.py
@@ -44,7 +44,7 @@
 ##########################################################################
 
 @pytest.mark.usefixtures("binary", "multiclass")
-class ClassificationReportTests(VisualTestCase, DatasetMixin):
+class TestClassificationReport(VisualTestCase, DatasetMixin):
     """
     ClassificationReport visualizer tests
     """
diff --git a/tests/test_classifier/test_confusion_matrix.py b/tests/test_classifier/test_confusion_matrix.py
--- a/tests/test_classifier/test_confusion_matrix.py
+++ b/tests/test_classifier/test_confusion_matrix.py
@@ -68,7 +68,7 @@ def digits(request):
 ##########################################################################
 
 @pytest.mark.usefixtures("digits")
-class ConfusionMatrixTests(VisualTestCase):
+class TestConfusionMatrix(VisualTestCase):
     """
     Test ConfusionMatrix visualizer
     """
diff --git a/tests/test_classifier/test_rocauc.py b/tests/test_classifier/test_rocauc.py
--- a/tests/test_classifier/test_rocauc.py
+++ b/tests/test_classifier/test_rocauc.py
@@ -52,12 +52,29 @@ class FakeClassifier(BaseEstimator, ClassifierMixin):
     pass
 
 
+def assert_valid_rocauc_scores(visualizer, nscores=4):
+    """
+    Assertion helper to ensure scores are correctly computed
+    """
+    __tracebackhide__ = True
+    assert len(visualizer.fpr.keys()) == nscores
+    assert len(visualizer.tpr.keys()) == nscores
+    assert len(visualizer.roc_auc.keys()) == nscores
+
+    for k in (0, 1, "micro", "macro"):
+        assert k in visualizer.fpr
+        assert k in visualizer.tpr
+        assert k in visualizer.roc_auc
+        assert len(visualizer.fpr[k]) == len(visualizer.tpr[k])
+        assert 0.0 < visualizer.roc_auc[k] < 1.0
+
+
 ##########################################################################
 ##  Tests
 ##########################################################################
 
 @pytest.mark.usefixtures("binary", "multiclass")
-class ROCAUCTests(VisualTestCase, DatasetMixin):
+class TestROCAUC(VisualTestCase, DatasetMixin):
 
     def test_binary_probability(self):
         """
@@ -74,20 +91,10 @@ def test_binary_probability(self):
         assert 0 <= s <= 1
 
         # Check the scores
-        self.assertEqual(len(visualizer.fpr.keys()), 4)
-        self.assertEqual(len(visualizer.tpr.keys()), 4)
-        self.assertEqual(len(visualizer.roc_auc.keys()), 4)
-
-        for k in (0, 1, "micro", "macro"):
-            self.assertIn(k, visualizer.fpr)
-            self.assertIn(k, visualizer.tpr)
-            self.assertIn(k, visualizer.roc_auc)
-            self.assertEqual(len(visualizer.fpr[k]), len(visualizer.tpr[k]))
-            self.assertGreater(visualizer.roc_auc[k], 0.0)
-            self.assertLess(visualizer.roc_auc[k], 1.0)
+        assert_valid_rocauc_scores(visualizer)
 
         # Compare the images
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=TOL)
 
     def test_binary_probability_decision(self):
@@ -105,20 +112,10 @@ def test_binary_probability_decision(self):
         assert 0 <= s <= 1
 
         # Check the scores
-        self.assertEqual(len(visualizer.fpr.keys()), 4)
-        self.assertEqual(len(visualizer.tpr.keys()), 4)
-        self.assertEqual(len(visualizer.roc_auc.keys()), 4)
-
-        for k in (0, 1, "micro", "macro"):
-            self.assertIn(k, visualizer.fpr)
-            self.assertIn(k, visualizer.tpr)
-            self.assertIn(k, visualizer.roc_auc)
-            self.assertEqual(len(visualizer.fpr[k]), len(visualizer.tpr[k]))
-            self.assertGreater(visualizer.roc_auc[k], 0.0)
-            self.assertLess(visualizer.roc_auc[k], 1.0)
+        assert_valid_rocauc_scores(visualizer)
 
         # Compare the images
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=TOL)
 
     def test_binary_decision(self):
@@ -136,13 +133,13 @@ def test_binary_decision(self):
         assert 0 <= s <= 1
 
         # Check the scores
-        self.assertEqual(len(visualizer.fpr.keys()), 1)
-        self.assertEqual(len(visualizer.tpr.keys()), 1)
-        self.assertEqual(len(visualizer.roc_auc.keys()), 1)
+        assert len(visualizer.fpr.keys()) == 1
+        assert len(visualizer.tpr.keys()) == 1
+        assert len(visualizer.roc_auc.keys()) == 1
 
         # Compare the images
         # NOTE: increased tolerance for both AppVeyor and Travis CI tests
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=10)
 
     def test_binary_micro_error(self):
@@ -154,7 +151,7 @@ def test_binary_micro_error(self):
         visualizer.fit(self.binary.X.train, self.binary.y.train)
 
         # Ensure score raises error (micro curves aren't defined for binary decisions)
-        with self.assertRaises(ModelError):
+        with pytest.raises(ModelError):
             visualizer.score(self.binary.X.test, self.binary.y.test)
 
     def test_binary_macro_error(self):
@@ -166,7 +163,7 @@ def test_binary_macro_error(self):
         visualizer.fit(self.binary.X.train, self.binary.y.train)
 
         # Ensure score raises error (macro curves aren't defined for binary decisions)
-        with self.assertRaises(ModelError):
+        with pytest.raises(ModelError):
             visualizer.score(self.binary.X.test, self.binary.y.test)
 
     def test_binary_per_class_error(self):
@@ -178,7 +175,7 @@ def test_binary_per_class_error(self):
         visualizer.fit(self.binary.X.train, self.binary.y.train)
 
         # Ensure score raises error (per_class curves not defined for binary decisions)
-        with self.assertRaises(ModelError):
+        with pytest.raises(ModelError):
             visualizer.score(self.binary.X.test, self.binary.y.test)
 
     def test_multiclass_rocauc(self):
@@ -196,20 +193,10 @@ def test_multiclass_rocauc(self):
         assert 0 <= s <= 1
 
         # Check the scores
-        self.assertEqual(len(visualizer.fpr.keys()), 8)
-        self.assertEqual(len(visualizer.tpr.keys()), 8)
-        self.assertEqual(len(visualizer.roc_auc.keys()), 8)
-
-        for k in (0, 1, "micro", "macro"):
-            self.assertIn(k, visualizer.fpr)
-            self.assertIn(k, visualizer.tpr)
-            self.assertIn(k, visualizer.roc_auc)
-            self.assertEqual(len(visualizer.fpr[k]), len(visualizer.tpr[k]))
-            self.assertGreater(visualizer.roc_auc[k], 0.0)
-            self.assertLess(visualizer.roc_auc[k], 1.0)
+        assert_valid_rocauc_scores(visualizer, nscores=8)
 
         # Compare the images
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=TOL)
 
     def test_rocauc_quickmethod(self):
@@ -232,15 +219,15 @@ def test_rocauc_no_micro(self):
 
         # Score the visualizer (should be the macro average)
         s = visualizer.score(self.binary.X.test, self.binary.y.test)
-        self.assertAlmostEqual(s, 0.8)
+        assert s == pytest.approx(0.8)
 
         # Assert that there is no micro score
-        self.assertNotIn("micro", visualizer.fpr)
-        self.assertNotIn("micro", visualizer.tpr)
-        self.assertNotIn("micro", visualizer.roc_auc)
+        assert "micro" not in visualizer.fpr
+        assert "micro" not in visualizer.tpr
+        assert "micro" not in visualizer.roc_auc
 
         # Compare the images
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=TOL)
 
     def test_rocauc_no_macro(self):
@@ -253,15 +240,15 @@ def test_rocauc_no_macro(self):
 
         # Score the visualizer (should be the micro average)
         s = visualizer.score(self.binary.X.test, self.binary.y.test)
-        self.assertAlmostEqual(s, 0.8)
+        assert s == pytest.approx(0.8)
 
         # Assert that there is no macro score
-        self.assertNotIn("macro", visualizer.fpr)
-        self.assertNotIn("macro", visualizer.tpr)
-        self.assertNotIn("macro", visualizer.roc_auc)
+        assert "macro" not in visualizer.fpr
+        assert "macro" not in visualizer.tpr
+        assert "macro" not in visualizer.roc_auc
 
         # Compare the images
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=TOL)
 
     def test_rocauc_no_macro_no_micro(self):
@@ -274,20 +261,20 @@ def test_rocauc_no_macro_no_micro(self):
 
         # Score the visualizer (should be the F1 score)
         s = visualizer.score(self.binary.X.test, self.binary.y.test)
-        self.assertAlmostEqual(s, 0.8)
+        assert s == pytest.approx(0.8)
 
         # Assert that there is no macro score
-        self.assertNotIn("macro", visualizer.fpr)
-        self.assertNotIn("macro", visualizer.tpr)
-        self.assertNotIn("macro", visualizer.roc_auc)
+        assert "macro" not in visualizer.fpr
+        assert "macro" not in visualizer.tpr
+        assert "macro" not in visualizer.roc_auc
 
         # Assert that there is no micro score
-        self.assertNotIn("micro", visualizer.fpr)
-        self.assertNotIn("micro", visualizer.tpr)
-        self.assertNotIn("micro", visualizer.roc_auc)
+        assert "micro" not in visualizer.fpr
+        assert "micro" not in visualizer.tpr
+        assert "micro" not in visualizer.roc_auc
 
         # Compare the images
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=TOL)
 
     def test_rocauc_no_classes(self):
@@ -300,16 +287,16 @@ def test_rocauc_no_classes(self):
 
         # Score the visualizer (should be the micro average)
         s = visualizer.score(self.binary.X.test, self.binary.y.test)
-        self.assertAlmostEqual(s, 0.8)
+        assert s == pytest.approx(0.8)
 
         # Assert that there still are per-class scores
         for c in (0, 1):
-            self.assertIn(c, visualizer.fpr)
-            self.assertIn(c, visualizer.tpr)
-            self.assertIn(c, visualizer.roc_auc)
+            assert c in visualizer.fpr
+            assert c in visualizer.tpr
+            assert c in visualizer.roc_auc
 
         # Compare the images
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=TOL)
 
     def test_rocauc_no_curves(self):
@@ -336,7 +323,7 @@ def test_rocauc_label_encoded(self):
 
         # Score the visualizer
         visualizer.score(self.multiclass.X.test, self.multiclass.y.test)
-        self.assertEqual(list(visualizer.classes_), class_labels)
+        assert list(visualizer.classes_) == class_labels
 
     def test_rocauc_not_label_encoded(self):
         """
@@ -352,7 +339,7 @@ def test_rocauc_not_label_encoded(self):
         visualizer.fit(self.multiclass.X.train, y_train)
 
         # Confirm that y_train and y_test have the same targets before calling score
-        self.assertEqual(set(y_train), set(y_test))
+        assert set(y_train) == set(y_test)
 
     def test_binary_decision_function_rocauc(self):
         """
@@ -360,7 +347,7 @@ def test_binary_decision_function_rocauc(self):
         """
         # Load the model and assert there is no predict_proba method.
         model = LinearSVC()
-        with self.assertRaises(AttributeError):
+        with pytest.raises(AttributeError):
             model.predict_proba
 
         # Fit model and visualizer
@@ -384,7 +371,7 @@ def test_multi_decision_function_rocauc(self):
         """
         # Load the model and assert there is no predict_proba method.
         model = LinearSVC()
-        with self.assertRaises(AttributeError):
+        with pytest.raises(AttributeError):
             model.predict_proba
 
         # Fit model and visualizer
@@ -412,7 +399,7 @@ def test_predict_proba_rocauc(self):
         """
         # Load the model and assert there is no decision_function method.
         model = GaussianNB()
-        with self.assertRaises(AttributeError):
+        with pytest.raises(AttributeError):
             model.decision_function
 
         # Fit model and visualizer
@@ -444,5 +431,5 @@ def test_no_scoring_function(self):
         Test ROCAUC with classifiers that have no scoring method
         """
         visualizer = ROCAUC(FakeClassifier())
-        with self.assertRaises(ModelError):
+        with pytest.raises(ModelError):
             visualizer._get_y_scores(self.binary.X.train)
diff --git a/tests/test_classifier/test_threshold.py b/tests/test_classifier/test_threshold.py
--- a/tests/test_classifier/test_threshold.py
+++ b/tests/test_classifier/test_threshold.py
@@ -72,7 +72,7 @@ def test_binary_discrimination_threshold(self):
         visualizer = DiscriminationThreshold(model, ax=ax, random_state=23)
 
         visualizer.fit(X, y)
-        visualizer.poof()
+        visualizer.finalize()
 
         self.assert_images_similar(visualizer)
 
@@ -119,7 +119,7 @@ def test_pandas_integration(self):
             LogisticRegression(), ax=ax, classes=classes, random_state=193
         )
         viz.fit(X, y)
-        viz.poof()
+        viz.finalize()
 
         self.assert_images_similar(viz, tol=0.1)
 
@@ -184,7 +184,7 @@ def test_binary_discrimination_threshold_alt_args(self):
         )
 
         visualizer.fit(X, y)
-        visualizer.poof()
+        visualizer.finalize()
 
         for metric in exclude:
             assert metric not in visualizer.cv_scores_
diff --git a/tests/test_cluster/test_base.py b/tests/test_cluster/test_base.py
--- a/tests/test_cluster/test_base.py
+++ b/tests/test_cluster/test_base.py
@@ -17,7 +17,7 @@
 ## Imports
 ##########################################################################
 
-import unittest
+import pytest
 
 from yellowbrick.exceptions import YellowbrickTypeError
 from yellowbrick.cluster.base import ClusteringScoreVisualizer
@@ -28,30 +28,31 @@
 from sklearn.cluster import KMeans, MiniBatchKMeans, AffinityPropagation
 from sklearn.cluster import MeanShift, DBSCAN, Birch
 
+
 ##########################################################################
 ## Clustering Base Test Cases
 ##########################################################################
 
-class ClusterBaseTests(unittest.TestCase):
+class TestClusterBase(object):
+
+    @pytest.mark.parametrize("model", [
+        SVC, SVR, Ridge, RidgeCV, LinearRegression, RandomForestClassifier
+    ])
+    def test_clusterer_enforcement_raises(self, model):
+        """
+        Assert that non-cluster models raise a TypeError for cluster visualizers
+        """
+        with pytest.raises(YellowbrickTypeError):
+            ClusteringScoreVisualizer(model())
 
-    def test_clusterer_enforcement(self):
+    @pytest.mark.parametrize("model", [
+        KMeans, MiniBatchKMeans, AffinityPropagation, MeanShift, DBSCAN, Birch
+    ])
+    def test_clusterer_enforcement(self, model):
         """
         Assert that only clustering estimators can be passed to cluster viz
         """
-        nomodels = [
-            SVC, SVR, Ridge, RidgeCV, LinearRegression, RandomForestClassifier
-        ]
-
-        for nomodel in nomodels:
-            with self.assertRaises(YellowbrickTypeError):
-                ClusteringScoreVisualizer(nomodel())
-
-        models = [
-            KMeans, MiniBatchKMeans, AffinityPropagation, MeanShift, DBSCAN, Birch
-        ]
-
-        for model in models:
-            try:
-                ClusteringScoreVisualizer(model())
-            except YellowbrickTypeError:
-                self.fail("could not pass clustering estimator to visualizer")
+        try:
+            ClusteringScoreVisualizer(model())
+        except YellowbrickTypeError:
+            self.fail("could not pass clustering estimator to visualizer")
diff --git a/tests/test_cluster/test_elbow.py b/tests/test_cluster/test_elbow.py
--- a/tests/test_cluster/test_elbow.py
+++ b/tests/test_cluster/test_elbow.py
@@ -143,7 +143,7 @@ def test_integrated_kmeans_elbow(self):
 
             visualizer = KElbowVisualizer(KMeans(random_state=42), k=4, ax=ax)
             visualizer.fit(X)
-            visualizer.poof()
+            visualizer.finalize()
 
             self.assert_images_similar(visualizer)
         except Exception as e:
@@ -157,7 +157,7 @@ def test_integrated_mini_batch_kmeans_elbow(self):
         # NOTE #182: cannot use occupancy dataset because of memory usage
 
         # Generate a blobs data set
-        X,y = make_blobs(
+        X, y = make_blobs(
             n_samples=1000, n_features=12, centers=6, shuffle=True, random_state=42
         )
 
@@ -168,7 +168,7 @@ def test_integrated_mini_batch_kmeans_elbow(self):
                 MiniBatchKMeans(random_state=42), k=4, ax=ax
             )
             visualizer.fit(X)
-            visualizer.poof()
+            visualizer.finalize()
 
             self.assert_images_similar(visualizer)
         except Exception as e:
@@ -186,7 +186,7 @@ def test_topic_modeling_k_means(self):
         visualizer = KElbowVisualizer(KMeans(), k=(4, 8))
 
         visualizer.fit(docs)
-        visualizer.poof()
+        visualizer.finalize()
 
         self.assert_images_similar(visualizer)
 
@@ -236,7 +236,7 @@ def test_distortion_metric(self):
         expected = np.array([ 69.100065, 54.081571, 43.146921, 34.978487])
         assert len(visualizer.k_scores_) == 4
 
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer)
         assert_array_almost_equal(visualizer.k_scores_, expected)
 
@@ -255,7 +255,7 @@ def test_silhouette_metric(self):
         expected = np.array([ 0.691636,  0.456646,  0.255174,  0.239842])
         assert len(visualizer.k_scores_) == 4
 
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer)
         assert_array_almost_equal(visualizer.k_scores_, expected)
 
@@ -279,13 +279,13 @@ def test_calinski_harabasz_metric(self):
             40.952179227847012, 35.939494
         ])
 
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer)
         assert_array_almost_equal(visualizer.k_scores_, expected)
 
     def test_locate_elbow(self):
         """
-        Test the addition of locate_elbow to an image 
+        Test the addition of locate_elbow to an image
         """
         X,y = make_blobs(
             n_samples=1000, n_features=5, centers=3, shuffle=True, random_state=42
@@ -303,7 +303,7 @@ def test_locate_elbow(self):
             4286.479848, 12463.383743, 8766.999551, 6950.08391, 5865.79722
         ])
 
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, windows_tol=2.2)
         assert_array_almost_equal(visualizer.k_scores_, expected)
 
@@ -347,6 +347,6 @@ def test_timings(self):
 
         # call draw again which is normally called in fit
         visualizer.draw()
-        visualizer.poof()
+        visualizer.finalize()
 
         self.assert_images_similar(visualizer)
diff --git a/tests/test_cluster/test_silhouette.py b/tests/test_cluster/test_silhouette.py
--- a/tests/test_cluster/test_silhouette.py
+++ b/tests/test_cluster/test_silhouette.py
@@ -34,9 +34,9 @@
 ## SilhouetteVisualizer Test Cases
 ##########################################################################
 
-class SilhouetteVisualizerTests(VisualTestCase):
+class TestSilhouetteVisualizer(VisualTestCase):
     """
-    Silhouette Visualizer
+    Silhouette Visualizer Tests
     """
 
     @pytest.mark.xfail(
@@ -59,7 +59,7 @@ def test_integrated_kmeans_silhouette(self):
 
             visualizer = SilhouetteVisualizer(KMeans(random_state=0), ax=ax)
             visualizer.fit(X)
-            visualizer.poof()
+            visualizer.finalize()
 
             self.assert_images_similar(visualizer, remove_legend=True)
         except Exception as e:
@@ -85,7 +85,7 @@ def test_integrated_mini_batch_kmeans_silhouette(self):
 
             visualizer = SilhouetteVisualizer(MiniBatchKMeans(random_state=0), ax=ax)
             visualizer.fit(X)
-            visualizer.poof()
+            visualizer.finalize()
 
             self.assert_images_similar(visualizer, remove_legend=True)
         except Exception as e:
@@ -118,7 +118,7 @@ def test_colormap_silhouette(self):
 
             visualizer = SilhouetteVisualizer(MiniBatchKMeans(random_state=0), ax=ax, colormap='gnuplot')
             visualizer.fit(X)
-            visualizer.poof()
+            visualizer.finalize()
 
             self.assert_images_similar(visualizer, remove_legend=True)
         except Exception as e:
@@ -145,7 +145,7 @@ def test_colors_silhouette(self):
                 colors=['red', 'green', 'blue', 'indigo', 'cyan', 'lavender']
             )
             visualizer.fit(X)
-            visualizer.poof()
+            visualizer.finalize()
 
             self.assert_images_similar(visualizer, remove_legend=True)
         except Exception as e:
@@ -167,7 +167,7 @@ def test_colormap_as_colors_silhouette(self):
 
             visualizer = SilhouetteVisualizer(MiniBatchKMeans(random_state=0), ax=ax, colors='cool')
             visualizer.fit(X)
-            visualizer.poof()
+            visualizer.finalize()
 
             tol = 3.2 if sys.platform == "win32" else 0.01  # Fails on AppVeyor with RMS 3.143
             self.assert_images_similar(visualizer, remove_legend=True, tol=tol)
diff --git a/tests/test_contrib/test_classifier/test_boundaries.py b/tests/test_contrib/test_classifier/test_boundaries.py
--- a/tests/test_contrib/test_classifier/test_boundaries.py
+++ b/tests/test_contrib/test_classifier/test_boundaries.py
@@ -66,7 +66,7 @@
 
 
 @pytest.mark.filterwarnings('ignore')
-class DecisionBoundariesVisualizerTest(VisualTestCase):
+class TestDecisionBoundariesVisualizer(VisualTestCase):
     """
     Test DecisionBoundariesVisualizer
     """
@@ -102,24 +102,23 @@ def test_init(self):
         model = neighbors.KNeighborsClassifier(3)
         viz = DecisionBoundariesVisualizer(model)
 
-        self.assertEquals(viz.step_size, 0.0025)
-        self.assertEqual(viz.name, 'KNeighborsClassifier')
-        self.assertEqual(viz.estimator, model)
+        assert viz.step_size == 0.0025
+        assert viz.name == 'KNeighborsClassifier'
+        assert viz.estimator is model
 
-        self.assertIsNone(viz.classes_)
-        self.assertIsNone(viz.features_)
-        self.assertIsNotNone(viz.markers)
-        self.assertIsNotNone(viz.scatter_alpha)
-        self.assertTrue(viz.show_scatter)
-
-        self.assertIsNone(viz.Z)
-        self.assertIsNone(viz.xx)
-        self.assertIsNone(viz.yy)
-        self.assertIsNone(viz.class_labels)
-        self.assertIsNone(viz.title)
-        self.assertIsNone(viz.x)
-        self.assertIsNone(viz.y)
+        assert viz.classes_ is None
+        assert viz.features_ is None
+        assert viz.markers is not None
+        assert viz.scatter_alpha is not None
+        assert viz.show_scatter is True
 
+        assert viz.Z is None
+        assert viz.xx is None
+        assert viz.yy is None
+        assert viz.class_labels is None
+        assert viz.title is None
+        assert viz.x is None
+        assert viz.y is None
 
     def test_scatter_xy_and_features_raise_error(self):
         """
@@ -128,7 +127,7 @@ def test_scatter_xy_and_features_raise_error(self):
         model = neighbors.KNeighborsClassifier(3)
         features = ["temperature", "relative_humidity", "light"]
 
-        with self.assertRaises(YellowbrickValueError):
+        with pytest.raises(YellowbrickValueError):
             DecisionBoundariesVisualizer(
                 model, features=features, x='one', y='two'
             )
@@ -139,8 +138,7 @@ def test_scatter_xy_changes_to_features(self):
         """
         model = neighbors.KNeighborsClassifier(3)
         visualizer = DecisionBoundariesVisualizer(model, x='one', y='two')
-        self.assertEquals(visualizer.features_, ['one', 'two'])
-
+        assert visualizer.features_ == ['one', 'two']
 
     def test_fit(self):
         """
@@ -154,17 +152,17 @@ def test_fit(self):
         fitted_viz = viz.fit(X_two_cols, y=y)
 
         # assert that classes and labels are established
-        self.assertEqual(fitted_viz.classes_, {0: '0', 1: '1', 2: '2', 3: '3'})
-        self.assertEqual(fitted_viz.features_, ['Feature One', 'Feature Two'])
+        assert fitted_viz.classes_ == {0: '0', 1: '1', 2: '2', 3: '3'}
+        assert fitted_viz.features_ == ['Feature One', 'Feature Two']
 
         # assert that the fit method is called
         model.fit.assert_called_once_with(X_two_cols, y)
         # mock object is called twice in predict and reshape
-        self.assertEqual(len(model.predict.mock_calls), 2)
+        assert len(model.predict.mock_calls) == 2
 
         # test that attrs are set
-        self.assertIsNotNone(fitted_viz.ax)
-        self.assertIsNotNone(fitted_viz.Z_shape)
+        assert fitted_viz.ax is not None
+        assert fitted_viz.Z_shape is not None
 
     def test_fit_class_labels(self):
         """
@@ -174,11 +172,7 @@ def test_fit_class_labels(self):
         viz = DecisionBoundariesVisualizer(
             model, classes=['one', 'two', 'three', 'four'])
         fitted_viz = viz.fit(X_two_cols, y=y)
-        self.assertEquals(fitted_viz.classes_,
-                          {'three': '2',
-                           'four': '3',
-                           'two': '1',
-                           'one': '0'})
+        assert fitted_viz.classes_ == {'three': '2', 'four': '3', 'two': '1', 'one': '0'}
 
     def test_fit_class_labels_class_names_edge_case(self):
         """
@@ -187,7 +181,9 @@ def test_fit_class_labels_class_names_edge_case(self):
         model = neighbors.KNeighborsClassifier(3)
         viz = DecisionBoundariesVisualizer(
             model, classes=['one', 'two', 'three', 'four', 'five'])
-        self.assertRaises(YellowbrickTypeError, viz.fit, X_two_cols, y=y)
+
+        with pytest.raises(YellowbrickTypeError):
+            viz.fit(X_two_cols, y=y)
 
     def test_fit_features_assignment_None(self):
         """
@@ -195,9 +191,9 @@ def test_fit_features_assignment_None(self):
         """
         model = neighbors.KNeighborsClassifier(3)
         viz = DecisionBoundariesVisualizer(model)
-        self.assertIsNone(viz.features_)
+        assert viz.features_ is None
         fitted_viz = viz.fit(X_two_cols, y=y)
-        self.assertEquals(fitted_viz.features_, ['Feature One', 'Feature Two'])
+        assert fitted_viz.features_ == ['Feature One', 'Feature Two']
 
     def test_fit_features_assignment(self):
         """
@@ -206,7 +202,7 @@ def test_fit_features_assignment(self):
         model = neighbors.KNeighborsClassifier(3)
         viz = DecisionBoundariesVisualizer(model, features=['one', 'two'])
         fitted_viz = viz.fit(X_two_cols, y=y)
-        self.assertEquals(fitted_viz.features_, ['one', 'two'])
+        assert fitted_viz.features_ == ['one', 'two']
 
     @mock.patch("yellowbrick.contrib.classifier.boundaries.OrderedDict")
     def test_draw_ordereddict_calls(self, mock_odict):
@@ -216,8 +212,11 @@ def test_draw_ordereddict_calls(self, mock_odict):
         mock_odict.return_value = {}
         model = neighbors.KNeighborsClassifier(3)
         viz = DecisionBoundariesVisualizer(model, features=['one', 'two'])
-        self.assertRaises(KeyError, viz.fit_draw, X_two_cols, y=y)
-        self.assertEquals(len(mock_odict.mock_calls), 2)
+
+        with pytest.raises(KeyError):
+            viz.fit_draw(X_two_cols, y=y)
+
+        assert len(mock_odict.mock_calls) == 2
 
     @mock.patch("yellowbrick.contrib.classifier.boundaries.resolve_colors")
     def test_draw_ordereddict_calls_one(self, mock_resolve_colors):
@@ -227,8 +226,11 @@ def test_draw_ordereddict_calls_one(self, mock_resolve_colors):
         mock_resolve_colors.return_value = []
         model = neighbors.KNeighborsClassifier(3)
         viz = DecisionBoundariesVisualizer(model, features=['one', 'two'])
-        self.assertRaises(StopIteration, viz.fit_draw, X_two_cols, y=y)
-        self.assertEquals(len(mock_resolve_colors.mock_calls), 1)
+
+        with pytest.raises(StopIteration):
+            viz.fit_draw(X_two_cols, y=y)
+
+        assert len(mock_resolve_colors.mock_calls) == 1
 
     def test_draw_ax_show_scatter_true(self):
         """
@@ -243,9 +245,9 @@ def test_draw_ax_show_scatter_true(self):
         fitted_viz.ax.legend = mock.MagicMock()
 
         fitted_viz.draw(X_two_cols, y=y)
-        self.assertEquals(len(fitted_viz.ax.pcolormesh.mock_calls), 1)
-        self.assertEquals(len(fitted_viz.ax.scatter.mock_calls), 4)
-        self.assertEquals(len(fitted_viz.ax.legend.mock_calls), 0)
+        assert len(fitted_viz.ax.pcolormesh.mock_calls) == 1
+        assert len(fitted_viz.ax.scatter.mock_calls) == 4
+        assert len(fitted_viz.ax.legend.mock_calls) == 0
 
     def test_draw_ax_show_scatter_False(self):
         """
@@ -261,9 +263,9 @@ def test_draw_ax_show_scatter_False(self):
         fitted_viz.ax.legend = mock.MagicMock()
 
         fitted_viz.draw(X_two_cols, y=y)
-        self.assertEquals(len(fitted_viz.ax.pcolormesh.mock_calls), 1)
-        self.assertEquals(len(fitted_viz.ax.scatter.mock_calls), 0)
-        self.assertEquals(len(fitted_viz.ax.legend.mock_calls), 1)
+        assert len(fitted_viz.ax.pcolormesh.mock_calls) == 1
+        assert len(fitted_viz.ax.scatter.mock_calls) == 0
+        assert len(fitted_viz.ax.legend.mock_calls) == 1
 
     def test_finalize(self):
         """
@@ -280,7 +282,7 @@ def test_finalize(self):
         fitted_viz.ax.set_xlabel = mock.MagicMock()
         fitted_viz.ax.set_ylabel = mock.MagicMock()
 
-        fitted_viz.poof()
+        fitted_viz.finalize()
 
         fitted_viz.ax.legend.assert_called_once_with(loc='best', frameon=True)
         fitted_viz.ax.set_xlabel.assert_called_once_with('one')
@@ -345,7 +347,7 @@ def test_integrated_plot_numpy_named_arrays(self):
 
         visualizer = DecisionBoundariesVisualizer(model, features=['a', 'f'])
         visualizer.fit_draw_poof(X, y=y)
-        self.assertEquals(visualizer.features_, ['a', 'f'])
+        assert visualizer.features_ == ['a', 'f']
         self.assert_images_similar(visualizer)
 
     def test_integrated_scatter_numpy_arrays_no_names(self):
@@ -356,7 +358,7 @@ def test_integrated_scatter_numpy_arrays_no_names(self):
 
         visualizer = DecisionBoundariesVisualizer(model, features=[1, 2])
         visualizer.fit_draw_poof(X, y)
-        self.assertEquals(visualizer.features_, [1, 2])
+        assert visualizer.features_ == [1, 2]
 
     @pytest.mark.xfail(
         sys.platform == 'win32', reason="images not close on windows"
diff --git a/tests/test_contrib/test_missing/test_bar.py b/tests/test_contrib/test_missing/test_bar.py
--- a/tests/test_contrib/test_missing/test_bar.py
+++ b/tests/test_contrib/test_missing/test_bar.py
@@ -18,6 +18,9 @@
 ##########################################################################
 
 import os
+import pytest
+import numpy as np
+
 from tests.base import VisualTestCase
 from sklearn.datasets import make_classification
 from yellowbrick.contrib.missing.bar import *
@@ -27,21 +30,22 @@
 except ImportError:
     pd = None
 
+
+@pytest.fixture(scope="class")
+def missing_bar_tolerance(request):
+    request.cls.tol = 0.5 if os.name == 'nt' else 0.01
+
+
 ##########################################################################
 ## Feature Importances Tests
 ##########################################################################
 
+@pytest.mark.usefixtures("missing_bar_tolerance")
 class TestMissingBarVisualizer(VisualTestCase):
     """
     FeatureImportances visualizer
     """
 
-    def setUp(self):
-        super(TestMissingBarVisualizer, self).setUp()
-        self.tol = 0.01
-        if os.name == 'nt': # Windows
-            self.tol = 0.5
-
     def test_missingvaluesbar_pandas(self):
         """
         Integration test of visualizer with pandas
@@ -58,11 +62,10 @@ def test_missingvaluesbar_pandas(self):
         features = [str(n) for n in range(20)]
         viz = MissingValuesBar(features=features)
         viz.fit(X_)
-        viz.poof()
+        viz.finalize()
 
         self.assert_images_similar(viz, tol=self.tol)
 
-
     def test_missingvaluesbar_numpy(self):
         """
         Integration test of visualizer with numpy without target y passed in
@@ -78,7 +81,7 @@ def test_missingvaluesbar_numpy(self):
         features = [str(n) for n in range(20)]
         viz = MissingValuesBar(features=features)
         viz.fit(X)
-        viz.poof()
+        viz.finalize()
 
         self.assert_images_similar(viz, tol=self.tol)
 
@@ -98,7 +101,7 @@ def test_missingvaluesbar_numpy_with_y_target(self):
         features = [str(n) for n in range(20)]
         viz = MissingValuesBar(features=features)
         viz.fit(X, y)
-        viz.poof()
+        viz.finalize()
 
         self.assert_images_similar(viz, tol=self.tol)
 
@@ -118,6 +121,6 @@ def test_missingvaluesbar_numpy_with_y_target_with_labels(self):
         features = [str(n) for n in range(20)]
         viz = MissingValuesBar(features=features, classes=['class A', 'class B'])
         viz.fit(X, y)
-        viz.poof()
+        viz.finalize()
 
         self.assert_images_similar(viz, tol=self.tol)
diff --git a/tests/test_contrib/test_missing/test_dispersion.py b/tests/test_contrib/test_missing/test_dispersion.py
--- a/tests/test_contrib/test_missing/test_dispersion.py
+++ b/tests/test_contrib/test_missing/test_dispersion.py
@@ -16,7 +16,10 @@
 ##########################################################################
 ## Imports
 ##########################################################################
+
 import os
+import pytest
+
 from sklearn.datasets import make_classification
 from tests.base import VisualTestCase
 
@@ -27,20 +30,21 @@
 except ImportError:
     pd = None
 
+
+@pytest.fixture(scope="class")
+def missing_dispersion_tolerance(request):
+    request.cls.tol = 0.5 if os.name == 'nt' else 0.01
+
+
 ##########################################################################
 ## Feature Importances Tests
 ##########################################################################
 
-class MissingValuesDispersionTestCase(VisualTestCase):
+@pytest.mark.usefixtures("missing_dispersion_tolerance")
+class TestMissingValuesDispersion(VisualTestCase):
     """
     MissingValuesDispersion visualizer
     """
-    def setUp(self):
-        super(MissingValuesDispersionTestCase, self).setUp()
-        self.tol = 0.01
-        if os.name == 'nt': # Windows
-            self.tol = 5.0
-
 
     def test_missingvaluesdispersion_with_pandas(self):
         """
@@ -58,7 +62,7 @@ def test_missingvaluesdispersion_with_pandas(self):
         features = [str(n) for n in range(20)]
         viz = MissingValuesDispersion(features=features)
         viz.fit(X_)
-        viz.poof()
+        viz.finalize()
 
         self.assert_images_similar(viz, tol=self.tol)
 
@@ -79,11 +83,10 @@ def test_missingvaluesdispersion_with_pandas_with_y_targets(self):
         classes = ['Class A', 'Class B']
         viz = MissingValuesDispersion(features=features, classes=classes)
         viz.fit(X_, y=y)
-        viz.poof()
+        viz.finalize()
 
         self.assert_images_similar(viz, tol=self.tol)
 
-
     def test_missingvaluesdispersion_with_numpy(self):
         """
         Integration test of visualizer with numpy
@@ -99,7 +102,7 @@ def test_missingvaluesdispersion_with_numpy(self):
         features = [str(n) for n in range(20)]
         viz = MissingValuesDispersion(features=features)
         viz.fit(X)
-        viz.poof()
+        viz.finalize()
 
         self.assert_images_similar(viz, tol=self.tol)
 
@@ -119,6 +122,6 @@ def test_missingvaluesdispersion_with_numpy_with_y_targets(self):
         classes = ['Class A', 'Class B']
         viz = MissingValuesDispersion(features=features, classes=classes)
         viz.fit(X, y=y)
-        viz.poof()
+        viz.finalize()
 
         self.assert_images_similar(viz, tol=self.tol)
diff --git a/tests/test_contrib/test_scatter.py b/tests/test_contrib/test_scatter.py
--- a/tests/test_contrib/test_scatter.py
+++ b/tests/test_contrib/test_scatter.py
@@ -16,11 +16,10 @@
 # Imports
 ##########################################################################
 
-from unittest import mock
-
-import matplotlib as mpl
 import pytest
+import matplotlib as mpl
 
+from unittest import mock
 from tests.base import VisualTestCase
 from yellowbrick.contrib.scatter import *
 from yellowbrick.datasets import load_occupancy
@@ -38,7 +37,7 @@
 ##########################################################################
 
 @pytest.mark.filterwarnings('ignore')
-class ScatterVizTests(VisualTestCase):
+class TestScatterViz(VisualTestCase):
     """
     Test ScatterViz
     """
@@ -61,7 +60,7 @@ def test_init_alias(self):
         """
         features = ["temperature", "relative humidity"]
         visualizer = ScatterVisualizer(features=features, markers=['*'])
-        self.assertIsNotNone(visualizer.markers)
+        assert visualizer.markers is not None
 
     def test_scatter(self):
         """
@@ -89,7 +88,7 @@ def test_scatter_no_features(self):
         X_two_cols = self.X[:, :2]
         visualizer = ScatterViz()
         visualizer.fit_transform_poof(X_two_cols, self.y)
-        self.assertEquals(visualizer.features_, ['Feature One', 'Feature Two'])
+        assert visualizer.features_ == ['Feature One', 'Feature Two']
 
     def test_scatter_only_two_features_allowed_init(self):
         """
@@ -97,7 +96,7 @@ def test_scatter_only_two_features_allowed_init(self):
         """
         features = ["temperature", "relative humidity", "light"]
 
-        with self.assertRaises(YellowbrickValueError):
+        with pytest.raises(YellowbrickValueError):
             ScatterViz(features=features)
 
     def test_scatter_xy_and_features_raise_error(self):
@@ -106,7 +105,7 @@ def test_scatter_xy_and_features_raise_error(self):
         """
         features = ["temperature", "relative humidity", "light"]
 
-        with self.assertRaises(YellowbrickValueError):
+        with pytest.raises(YellowbrickValueError):
             ScatterViz(features=features, x='one', y='two')
 
     def test_scatter_xy_changes_to_features(self):
@@ -114,17 +113,15 @@ def test_scatter_xy_changes_to_features(self):
         Assert that x,y with no features will not raise scatterviz error
         """
         visualizer = ScatterViz(x='one', y='two')
-        self.assertEquals(visualizer.features_, ['one', 'two'])
+        assert visualizer.features_ == ['one', 'two']
 
     def test_scatter_requires_two_features_in_numpy_matrix(self):
         """
         Assert only two features allowed for scatter visualizer if not in init
         """
         visualizer = ScatterViz()
-        with self.assertRaises(YellowbrickValueError) as context:
+        with pytest.raises(YellowbrickValueError, match='only accepts two features'):
             visualizer.fit_transform(self.X, self.y)
-            self.assertTrue(
-                'only accepts two features' in str(context.exception))
 
     def test_integrated_scatter(self):
         """
@@ -170,7 +167,7 @@ def test_scatter_quick_method(self):
         ax = scatterviz(X[:, :2], y=y, ax=None, features=features)
 
         # test that is returns a matplotlib obj with axes
-        self.assertIsInstance(ax, mpl.axes.Axes)
+        assert isinstance(ax, mpl.axes.Axes)
 
     @pytest.mark.skipif(pd is None, reason="pandas is required for this test")
     def test_integrated_scatter_with_pandas(self):
@@ -205,7 +202,7 @@ def test_integrated_scatter_numpy_named_arrays(self):
         X_named = self.X.astype(dt, casting='unsafe')
         visualizer = ScatterViz(features=['one', 'two'])
         visualizer.fit_transform_poof(X_named, self.y)
-        self.assertEquals(visualizer.features_, ['one', 'two'])
+        assert visualizer.features_ == ['one', 'two']
 
     def test_integrated_scatter_numpy_arrays_no_names(self):
         """
@@ -213,7 +210,7 @@ def test_integrated_scatter_numpy_arrays_no_names(self):
         """
         visualizer = ScatterViz(features=[1, 2])
         visualizer.fit_transform_poof(self.X, self.y)
-        self.assertEquals(visualizer.features_, [1, 2])
+        assert visualizer.features_ == [1, 2]
 
     def test_scatter_image(self):
         """
diff --git a/tests/test_draw.py b/tests/test_draw.py
--- a/tests/test_draw.py
+++ b/tests/test_draw.py
@@ -36,14 +36,14 @@ def test_manual_legend_uneven_colors():
 
 @pytest.fixture(scope="class")
 def data(request):
-    
+
     data = np.array(
             [[4, 8, 7, 6, 5, 2, 1],
              [6, 7, 9, 6, 9, 3, 6],
              [5, 1, 6, 8, 4, 7, 8],
              [6, 8, 1, 5, 6, 7, 4]]
             )
-    
+
     request.cls.data = data
 
 ##########################################################################
@@ -83,80 +83,79 @@ def test_manual_legend(self):
 
     def test_vertical_bar_stack(self):
         """
-        Test bar_stack for vertical orientation 
+        Test bar_stack for vertical orientation
         """
         _, ax = plt.subplots()
-        
+
         # Plots stacked bar charts
         bar_stack(self.data, ax=ax, orientation='v')
-        
+
          # Assert image similarity
         self.assert_images_similar(ax=ax, tol=0.1)
-        
+
     def test_horizontal_bar_stack(self):
         """
-        Test bar_stack for horizontal orientation 
+        Test bar_stack for horizontal orientation
         """
         _, ax = plt.subplots()
         # Plots stacked bar charts
         bar_stack(self.data, ax=ax, orientation='h')
-        
+
         # Assert image similarity
         self.assert_images_similar(ax=ax, tol=0.1)
-        
+
     def test_single_row_bar_stack(self):
         """
-        Test bar_stack for single row 
-        """        
+        Test bar_stack for single row
+        """
         data = np.array([[4, 8, 7, 6, 5, 2, 1]])
-        
+
         _, ax = plt.subplots()
-        
+
         # Plots stacked bar charts
         bar_stack(data, ax=ax)
-        
+
         # Assert image similarity
         self.assert_images_similar(ax=ax, tol=0.1)
-    
+
     def test_labels_vertical(self):
         """
         Test labels and ticks for vertical barcharts
-        """        
+        """
         labels =  ['books', 'cinema', 'cooking', 'gaming']
-        ticks = ['noun', 'verb', 'adverb', 'pronoun', 'preposition', 
+        ticks = ['noun', 'verb', 'adverb', 'pronoun', 'preposition',
                  'digit', 'other']
         _, ax = plt.subplots()
-        
+
         # Plots stacked bar charts
-        bar_stack(self.data, labels = labels, ticks=ticks, 
+        bar_stack(self.data, labels = labels, ticks=ticks,
                   colors=['r','b','g','y'])
-        
+
         # Extract tick labels from the plot
         ticks_ax = [tick.get_text() for tick in ax.xaxis.get_ticklabels()]
         #Assert that ticks are set properly
         assert ticks_ax==ticks
-        
+
         # Assert image similarity
         self.assert_images_similar(ax=ax, tol=0.05)
-    
+
     def test_labels_horizontal(self):
         """
         Test labels and ticks with horizontal barcharts
-        """        
+        """
         labels =  ['books', 'cinema', 'cooking', 'gaming']
-        ticks = ['noun', 'verb', 'adverb', 'pronoun', 'preposition', 
+        ticks = ['noun', 'verb', 'adverb', 'pronoun', 'preposition',
                  'digit', 'other']
         _, ax = plt.subplots()
-        
+
         # Plots stacked bar charts
-        bar_stack(self.data, labels = labels, ticks=ticks, orientation='h', 
+        bar_stack(self.data, labels = labels, ticks=ticks, orientation='h',
                   colormap='cool')
-        
+
         # Extract tick labels from the plot
         ticks_ax = [tick.get_text() for tick in ax.yaxis.get_ticklabels()]
         #Assert that ticks are set properly
         assert ticks_ax==ticks
-        
+
         # Assert image similarity
         self.assert_images_similar(ax=ax, tol=0.05)
-        
\ No newline at end of file
diff --git a/tests/test_features/test_base.py b/tests/test_features/test_base.py
--- a/tests/test_features/test_base.py
+++ b/tests/test_features/test_base.py
@@ -28,22 +28,13 @@
 ## FeatureVisualizer Base Tests
 ##########################################################################
 
-class FeatureVisualizerBaseTests(VisualTestCase):
+class TestFeatureVisualizerBase(VisualTestCase):
 
     def test_subclass(self):
         """
         Assert the feature visualizer is in its rightful place
         """
         visualizer = FeatureVisualizer()
-        self.assertIsInstance(visualizer, TransformerMixin)
-        self.assertIsInstance(visualizer, BaseEstimator)
-        self.assertIsInstance(visualizer, Visualizer)
-
-    # def test_interface(self):
-    #     """
-    #     Test the feature visualizer interface
-    #     """
-    #
-    #     visualizer = FeatureVisualizer()
-    #     with self.assertRaises(NotImplementedError):
-    #         visualizer.poof()
+        assert isinstance(visualizer, TransformerMixin)
+        assert isinstance(visualizer, BaseEstimator)
+        assert isinstance(visualizer, Visualizer)
diff --git a/tests/test_features/test_jointplot.py b/tests/test_features/test_jointplot.py
--- a/tests/test_features/test_jointplot.py
+++ b/tests/test_features/test_jointplot.py
@@ -23,11 +23,12 @@
 ##########################################################################
 
 import sys
+import pytest
+import numpy as np
+
 from functools import partial
 from unittest.mock import patch, MagicMock
 
-import numpy as np
-import pytest
 from sklearn.datasets import make_classification, make_regression
 
 from tests.base import IS_WINDOWS_OR_CONDA, VisualTestCase
@@ -46,6 +47,7 @@
 except ImportError:
     pd = None
 
+
 ##########################################################################
 ## Fixtures
 ##########################################################################
diff --git a/tests/test_features/test_manifold.py b/tests/test_features/test_manifold.py
--- a/tests/test_features/test_manifold.py
+++ b/tests/test_features/test_manifold.py
@@ -44,60 +44,50 @@ class TestManifold(VisualTestCase):
     Test Manifold visualizer
     """
 
-    def test_manifold_construction(self):
+    @pytest.mark.parametrize("algorithm", [
+        "lle", "ltsa", "hessian", "modified", "isomap", "mds", "spectral", "tsne",
+    ])
+    def test_manifold_construction(self, algorithm):
         """
         Should be able to construct a manifold estimator from a string
         """
-        # TODO: parametrize this once unittest.TestCase dependency removed.
-        algorithms = [
-            "lle", "ltsa", "hessian", "modified",
-            "isomap", "mds", "spectral", "tsne",
-        ]
-
-        for algorithm in algorithms:
-            message = "case failed for {}".format(algorithm)
-            params = {
-                "n_neighbors": 18,
-                "random_state": 53,
-            }
-            oz = Manifold(manifold=algorithm, **params)
-            assert is_estimator(oz.manifold), message
-            assert oz.manifold.get_params()["n_components"] == 2, message
-
-            manifold_params = oz.manifold.get_params()
-            for param, value in params.items():
-                if param in manifold_params:
-                    assert value == manifold_params[param], message
-
-    def test_manifold_warning(self):
+        message = "case failed for {}".format(algorithm)
+        params = {
+            "n_neighbors": 18,
+            "random_state": 53,
+        }
+        oz = Manifold(manifold=algorithm, **params)
+        assert is_estimator(oz.manifold), message
+        assert oz.manifold.get_params()["n_components"] == 2, message
+
+        manifold_params = oz.manifold.get_params()
+        for param, value in params.items():
+            if param in manifold_params:
+                assert value == manifold_params[param], message
+
+    @pytest.mark.parametrize("algorithm", [
+        "lle", "ltsa", "hessian", "modified", "isomap", "spectral",
+    ])
+    def test_manifold_warning(self, algorithm):
         """
         Should raise a warning if n_neighbors not specified
         """
-        # TODO: parametrize this once unittest.TestCase dependency removed.
-        algorithms = [
-            "lle", "ltsa", "hessian", "modified", "isomap", "spectral",
-        ]
+        message = "case failed for {}".format(algorithm)
+        n_neighbors = 6 if algorithm == "hessian" else 5
 
-        for algorithm in algorithms:
-            message = "case failed for {}".format(algorithm)
-            n_neighbors = 6 if algorithm == "hessian" else 5
+        with pytest.warns(YellowbrickWarning):
+            oz = Manifold(manifold=algorithm)
+            assert oz.n_neighbors == n_neighbors, message
 
-            with pytest.warns(YellowbrickWarning):
-                oz = Manifold(manifold=algorithm)
-                assert oz.n_neighbors == n_neighbors, message
-
-    def test_manifold_no_warning(self):
+    @pytest.mark.parametrize("algorithm", ["mds", "tsne"])
+    def test_manifold_no_warning(self, algorithm):
         """
         Should not raise a warning if n_neighbors not specified
         """
-        # TODO: parametrize this once unittest.TestCase dependency removed.
-        algorithms = ["mds", "tsne"]
-
-        for algorithm in algorithms:
-            message = "case failed for {}".format(algorithm)
+        message = "case failed for {}".format(algorithm)
 
-            with pytest.warns(None) as record:
-                assert not record.list, message
+        with pytest.warns(None) as record:
+            assert not record.list, message
 
     def test_bad_manifold_exception(self):
         """
@@ -216,21 +206,16 @@ def test_manifold_pandas(self):
         self.assert_images_similar(oz, tol=35)
 
     @pytest.mark.filterwarnings("ignore:Conversion of the second argument")
-    def test_manifold_algorithm_fit(self):
+    @pytest.mark.parametrize("algorithm", [
+        "lle", "ltsa", "hessian", "modified", "isomap", "mds", "spectral", "tsne",
+    ])
+    def test_manifold_algorithm_fit(self, algorithm):
         """
         Test that all algorithms can be fitted correctly
         """
-        # TODO: parametrize this once unittest.TestCase dependency removed.
-        algorithms = [
-            "lle", "ltsa", "hessian", "modified",
-            "isomap", "mds", "spectral", "tsne",
-        ]
-
         X, y = make_s_curve(200, random_state=888)
-
-        for algorithm in algorithms:
-            oz = Manifold(manifold=algorithm, n_neighbors=10, random_state=223)
-            oz.fit(X, y)
+        oz = Manifold(manifold=algorithm, n_neighbors=10, random_state=223)
+        oz.fit(X, y)
 
     def test_determine_target_color_type(self):
         """
diff --git a/tests/test_features/test_pca.py b/tests/test_features/test_pca.py
--- a/tests/test_features/test_pca.py
+++ b/tests/test_features/test_pca.py
@@ -56,7 +56,7 @@ def binary(request):
 ##########################################################################
 
 @pytest.mark.usefixtures("binary")
-class PCADecompositionTests(VisualTestCase):
+class TestPCADecomposition(VisualTestCase):
     """
     Test the PCADecomposition visualizer
     """
@@ -191,7 +191,7 @@ def test_scale_true_3d_execption(self):
         with pytest.raises(ValueError, match=e):
             pca = PCADecomposition(**params)
             pca.fit(X)
-    
+
     @mock.patch('yellowbrick.features.pca.plt.sca', autospec=True)
     def test_alpha_param(self, mock_sca):
         """
@@ -202,7 +202,7 @@ def test_alpha_param(self, mock_sca):
         visualizer = PCADecomposition(**params).fit(self.dataset.X)
         pca_array = visualizer.transform(self.dataset.X)
         assert visualizer.alpha == 0.3
-        
+
         visualizer.ax = mock.MagicMock()
         visualizer.fit(self.dataset.X)
         visualizer.transform(self.dataset.X)
@@ -212,4 +212,4 @@ def test_alpha_param(self, mock_sca):
         assert "alpha" in scatter_kwargs
         assert scatter_kwargs["alpha"] == 0.3
         assert pca_array.shape == (self.dataset.X.shape[0], 2)
-        
+
diff --git a/tests/test_features/test_pcoords.py b/tests/test_features/test_pcoords.py
--- a/tests/test_features/test_pcoords.py
+++ b/tests/test_features/test_pcoords.py
@@ -70,7 +70,7 @@ def test_parallel_coords(self):
         """
         visualizer = ParallelCoordinates()
         visualizer.fit_transform(self.dataset.X, self.dataset.y)
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=0.25)
 
     def test_parallel_coords_fast(self):
@@ -79,7 +79,7 @@ def test_parallel_coords_fast(self):
         """
         visualizer = ParallelCoordinates(fast=True)
         visualizer.fit_transform(self.dataset.X, self.dataset.y)
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=0.25)
 
     def test_alpha(self):
@@ -88,7 +88,7 @@ def test_alpha(self):
         """
         visualizer = ParallelCoordinates(alpha=1.0)
         visualizer.fit_transform(self.dataset.X, self.dataset.y)
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=0.25)
 
     def test_alpha_fast(self):
@@ -97,7 +97,7 @@ def test_alpha_fast(self):
         """
         visualizer = ParallelCoordinates(alpha=1.0, fast=True)
         visualizer.fit_transform(self.dataset.X, self.dataset.y)
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=0.25)
 
     def test_labels(self):
@@ -108,7 +108,7 @@ def test_labels(self):
             classes=['a', 'b', 'c'], features=['f1', 'f2', 'f3', 'f4', 'f5']
         )
         visualizer.fit_transform(self.dataset.X, self.dataset.y)
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer)
 
     def test_labels_fast(self):
@@ -119,7 +119,7 @@ def test_labels_fast(self):
             classes=['a', 'b', 'c'], features=['f1', 'f2', 'f3', 'f4', 'f5'], fast=True
         )
         visualizer.fit_transform(self.dataset.X, self.dataset.y)
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer)
 
     def test_normalized_l2(self):
@@ -128,7 +128,7 @@ def test_normalized_l2(self):
         """
         visualizer = ParallelCoordinates(normalize='l2')
         visualizer.fit_transform(self.dataset.X, self.dataset.y)
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=0.25)
 
     def test_normalized_l2_fast(self):
@@ -137,7 +137,7 @@ def test_normalized_l2_fast(self):
         """
         visualizer = ParallelCoordinates(normalize='l2', fast=True)
         visualizer.fit_transform(self.dataset.X, self.dataset.y)
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=0.25)
 
     def test_normalized_minmax(self):
@@ -146,7 +146,7 @@ def test_normalized_minmax(self):
         """
         visualizer = ParallelCoordinates(normalize='minmax')
         visualizer.fit_transform(self.dataset.X, self.dataset.y)
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=0.25)
 
     def test_normalized_minmax_fast(self):
@@ -155,7 +155,7 @@ def test_normalized_minmax_fast(self):
         """
         visualizer = ParallelCoordinates(normalize='minmax', fast=True)
         visualizer.fit_transform(self.dataset.X, self.dataset.y)
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=0.25)
 
     @pytest.mark.skipif(pd is None, reason="test requires pandas")
@@ -174,7 +174,7 @@ def test_pandas_integration_sampled(self):
             sample=0.05, shuffle=True, random_state=4291, classes=classes
         )
         oz.fit_transform(X, y)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz, tol=0.1)
 
@@ -193,7 +193,7 @@ def test_numpy_integration_sampled(self):
             sample=0.05, shuffle=True, random_state=4291, classes=classes
         )
         oz.fit_transform(X, y)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz, tol=0.1)
 
@@ -211,7 +211,7 @@ def test_pandas_integration_fast(self):
 
         oz = ParallelCoordinates(fast=True, classes=classes)
         oz.fit_transform(X, y)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz, tol=0.1)
 
@@ -228,7 +228,7 @@ def test_numpy_integration_fast(self):
 
         oz = ParallelCoordinates(fast=True, classes=classes)
         oz.fit_transform(X, y)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz, tol=0.1)
 
@@ -236,7 +236,7 @@ def test_normalized_invalid_arg(self):
         """
         Invalid argument to 'normalize' should raise
         """
-        with self.assertRaises(YellowbrickValueError):
+        with pytest.raises(YellowbrickValueError):
             ParallelCoordinates(normalize='foo')
 
     def test_sample_int(self):
@@ -276,7 +276,7 @@ def test_sample_int_invalid(self):
         """
         Negative int values should raise exception
         """
-        with self.assertRaises(YellowbrickValueError):
+        with pytest.raises(YellowbrickValueError):
             ParallelCoordinates(sample=-1)
 
     def test_sample_float(self):
@@ -316,16 +316,17 @@ def test_sample_float_invalid(self):
         """
         Float values for 'sample' argument outside [0,1] should raise.
         """
-        with self.assertRaises(YellowbrickValueError):
+        with pytest.raises(YellowbrickValueError):
             ParallelCoordinates(sample=-0.2)
-        with self.assertRaises(YellowbrickValueError):
+
+        with pytest.raises(YellowbrickValueError):
             ParallelCoordinates(sample=1.1)
 
     def test_sample_invalid_type(self):
         """
         Non-numeric values for 'sample' argument should raise.
         """
-        with self.assertRaises(YellowbrickTypeError):
+        with pytest.raises(YellowbrickTypeError):
             ParallelCoordinates(sample='foo')
 
     @staticmethod
diff --git a/tests/test_features/test_radviz.py b/tests/test_features/test_radviz.py
--- a/tests/test_features/test_radviz.py
+++ b/tests/test_features/test_radviz.py
@@ -96,7 +96,7 @@ def test_radviz(self):
         """
         visualizer = RadViz()
         visualizer.fit_transform(self.dataset.X, self.dataset.y)
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=0.25)
 
     def test_radviz_alpha(self):
@@ -105,7 +105,7 @@ def test_radviz_alpha(self):
         """
         visualizer = RadViz(alpha=0.5)
         visualizer.fit_transform(self.dataset.X, self.dataset.y)
-        visualizer.poof()
+        visualizer.finalize()
         self.assert_images_similar(visualizer, tol=0.25)
 
     @pytest.mark.xfail(
diff --git a/tests/test_features/test_rfecv.py b/tests/test_features/test_rfecv.py
--- a/tests/test_features/test_rfecv.py
+++ b/tests/test_features/test_rfecv.py
@@ -107,7 +107,7 @@ def test_rfecv_classification(self):
         cv = ShuffleSplit(3, random_state=21)
         oz = RFECV(SVC(kernel="linear", C=1), cv=cv)
         oz.fit(self.dataset.X, self.dataset.y)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz, remove_legend=True)
 
@@ -144,7 +144,7 @@ def test_pandas_integration(self):
         cv = StratifiedKFold(n_splits=4, random_state=32)
         oz = RFECV(RandomForestClassifier(random_state=83), cv=cv)
         oz.fit(X, y)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz, remove_legend=True)
 
@@ -164,17 +164,17 @@ def test_numpy_integration(self):
         cv = StratifiedKFold(n_splits=4, random_state=32)
         oz = RFECV(RandomForestClassifier(random_state=83), cv=cv)
         oz.fit(X, y)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz, remove_legend=True)
 
-    def test_invalid_step(self):
+    @pytest.mark.parametrize("step", [0, -1, -5])
+    def test_invalid_step(self, step):
         """
         Test step hyperparam validation
         """
-        # TODO: parametrize when unittest is removed
         with pytest.raises(YellowbrickValueError, match="step must be >0"):
-            oz = RFECV(SVC(kernel="linear"), step=-1)
+            oz = RFECV(SVC(kernel="linear"), step=step)
             oz.fit(self.dataset.X, self.dataset.y)
 
     def test_rfecv_step(self):
diff --git a/tests/test_meta.py b/tests/test_meta.py
--- a/tests/test_meta.py
+++ b/tests/test_meta.py
@@ -113,7 +113,7 @@ def test_missing_baseline_image(self):
         Test that a missing basline image raises an exception
         """
         viz = RandomVisualizer(random_state=14).fit()
-        viz.poof()
+        viz.finalize()
 
         # Assert the baseline image does not exist
         assert_path_not_exists(
@@ -133,7 +133,7 @@ def test_random_visualizer(self):
         Test that a random visualization is correctly compared to a baseline
         """
         viz = RandomVisualizer(random_state=111).fit()
-        viz.poof()
+        viz.finalize()
 
         assert mpl.get_backend() == 'agg'
 
@@ -147,7 +147,7 @@ def test_random_visualizer_not_close(self):
         """
         # Baseline image random_state=225
         viz = RandomVisualizer(random_state=224).fit()
-        viz.poof()
+        viz.finalize()
 
         with pytest.raises(ImageComparisonFailure, match="images not close"):
             self.assert_images_similar(viz)
@@ -162,6 +162,6 @@ def test_random_visualizer_increased_tolerance(self):
         Test that not close visualizers pass with increased tolerance
         """
         viz = RandomVisualizer(random_state=224).fit()
-        viz.poof()
+        viz.finalize()
 
         self.assert_images_similar(viz, tol=30)
diff --git a/tests/test_model_selection/test_cross_validation.py b/tests/test_model_selection/test_cross_validation.py
--- a/tests/test_model_selection/test_cross_validation.py
+++ b/tests/test_model_selection/test_cross_validation.py
@@ -80,7 +80,7 @@ def test_classifier(self):
         )
 
         oz.fit(X, y)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz, tol=2.0)
 
@@ -120,7 +120,7 @@ def test_regression(self):
         )
 
         oz.fit(X, y)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz, tol=36.0)
 
@@ -178,6 +178,6 @@ def test_pandas_integration(self):
         oz = CVScores(BernoulliNB(), cv=cv)
 
         oz.fit(X, y)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz, tol=2.0)
diff --git a/tests/test_model_selection/test_learning_curve.py b/tests/test_model_selection/test_learning_curve.py
--- a/tests/test_model_selection/test_learning_curve.py
+++ b/tests/test_model_selection/test_learning_curve.py
@@ -83,7 +83,7 @@ def test_classifier(self):
         oz = LearningCurve(
             RandomForestClassifier(random_state=21), random_state=12
         ).fit(X, y)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz)
 
@@ -98,7 +98,7 @@ def test_regressor(self):
 
         oz = LearningCurve(Ridge(), random_state=18)
         oz.fit(X, y)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz)
 
@@ -111,7 +111,7 @@ def test_clusters(self):
         oz = LearningCurve(
             MiniBatchKMeans(random_state=281), random_state=182
         ).fit(X)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz, tol=10)
 
@@ -153,7 +153,7 @@ def test_pandas_integration(self):
         cv = StratifiedKFold(n_splits=4, random_state=32)
         oz = LearningCurve(GaussianNB(), cv=cv, random_state=23)
         oz.fit(X, y)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz)
 
diff --git a/tests/test_model_selection/test_validation_curve.py b/tests/test_model_selection/test_validation_curve.py
--- a/tests/test_model_selection/test_validation_curve.py
+++ b/tests/test_model_selection/test_validation_curve.py
@@ -90,7 +90,7 @@ def test_classifier(self):
         )
 
         oz.fit(X, y)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz)
 
@@ -109,7 +109,7 @@ def test_regression(self):
         )
 
         oz.fit(X, y)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz, tol=12.0)
 
@@ -155,7 +155,7 @@ def test_pandas_integration(self):
             BernoulliNB(), cv=cv, param_range=pr, param_name='alpha'
         )
         oz.fit(X, y)
-        oz.poof()
+        oz.finalize()
 
         self.assert_images_similar(oz)
 
diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py
--- a/tests/test_pipeline.py
+++ b/tests/test_pipeline.py
@@ -18,7 +18,7 @@
 ##########################################################################
 
 import os
-import unittest
+import pytest
 
 from unittest import mock
 from yellowbrick.base import Visualizer
@@ -40,6 +40,7 @@ class MockEstimator(BaseEstimator):
     def fit(self, X, y=None, **kwargs):
         return self
 
+
 class MockVisualEstimator(Visualizer):
 
     def fit(self, X, y=None, **kwargs):
@@ -76,7 +77,7 @@ def draw(self, **kwargs):
 ## VisualPipeline Tests
 ##########################################################################
 
-class VisualPipelineTests(unittest.TestCase):
+class TestVisualPipeline(object):
 
     def test_validate_steps(self):
         """
@@ -87,7 +88,7 @@ def test_validate_steps(self):
         # TypeError if the steps don't match transforms --> estimator.
 
         # validate a bad intermediate transformer on the Pipeline
-        with self.assertRaises(TypeError):
+        with pytest.raises(TypeError):
             Pipeline([
                 ('real', MockTransformer()),
                 ('bad', Thing()),
@@ -95,7 +96,7 @@ def test_validate_steps(self):
             ])
 
         # validate a bad intermediate transformer on the VisualPipeline
-        with self.assertRaises(TypeError):
+        with pytest.raises(TypeError):
             VisualPipeline([
                 ('real', MockTransformer()),
                 ('bad', Thing()),
@@ -103,14 +104,14 @@ def test_validate_steps(self):
             ])
 
         # validate a bad final estimator on the Pipeline
-        with self.assertRaises(TypeError):
+        with pytest.raises(TypeError):
             Pipeline([
                 ('real', MockTransformer()),
                 ('bad', Thing()),
             ])
 
         # validate a bad final estimator on the VisualPipeline
-        with self.assertRaises(TypeError):
+        with pytest.raises(TypeError):
             VisualPipeline([
                 ('real', MockTransformer()),
                 ('bad', Thing()),
@@ -149,11 +150,11 @@ def test_visual_steps_property(self):
             ('e', MockEstimator()),
         ])
 
-        self.assertNotIn('a', pipeline.visual_steps)
-        self.assertIn('b', pipeline.visual_steps)
-        self.assertNotIn('c', pipeline.visual_steps)
-        self.assertIn('d', pipeline.visual_steps)
-        self.assertNotIn('e', pipeline.visual_steps)
+        assert 'a' not in pipeline.visual_steps
+        assert 'b' in pipeline.visual_steps
+        assert 'c' not in pipeline.visual_steps
+        assert 'd' in pipeline.visual_steps
+        assert 'e' not in pipeline.visual_steps
 
     def test_pipeline_poof(self):
         """
@@ -192,7 +193,7 @@ def test_pipeline_savefig_poof(self):
         pipeline.steps[3][1].poof.assert_called_once_with(outpath=os.path.join(tmpdir, "d.pdf"))
         pipeline.steps[4][1].poof.assert_called_once_with(outpath=os.path.join(tmpdir, "e.pdf"))
 
-    @unittest.skip("need to find a way for fit to return self in mocks")
+    @pytest.mark.skip(reason="need to find a way for fit to return self in mocks")
     def test_fit_transform_poof_and_draw_calls(self):
         """
         Test calling fit, transform, and poof on the pipeline
diff --git a/tests/test_regressor/test_alphas.py b/tests/test_regressor/test_alphas.py
--- a/tests/test_regressor/test_alphas.py
+++ b/tests/test_regressor/test_alphas.py
@@ -29,6 +29,8 @@
 from yellowbrick.exceptions import YellowbrickValueError
 
 from sklearn.svm import SVR, SVC
+from sklearn.cluster import KMeans
+from sklearn.decomposition import PCA
 from sklearn.datasets import make_regression
 from sklearn.linear_model import Ridge, RidgeCV
 from sklearn.linear_model import Lasso, LassoCV
@@ -57,33 +59,35 @@ def test_similar_image(self):
 
         X, y = make_regression(random_state=0)
         visualizer.fit(X, y)
-        visualizer.poof()
+        visualizer.finalize()
 
         self.assert_images_similar(visualizer)
 
-    def test_regressor_cv(self):
+    @pytest.mark.parametrize("model", [SVR, Ridge, Lasso, LassoLars, ElasticNet])
+    def test_regressor_nocv(self, model):
         """
         Ensure only "CV" regressors are allowed
         """
-        # TODO: parametrize with models when unittest dependency removed
-        for model in (SVR, Ridge, Lasso, LassoLars, ElasticNet):
-            with pytest.raises(YellowbrickTypeError):
-                AlphaSelection(model())
+        with pytest.raises(YellowbrickTypeError):
+            AlphaSelection(model())
 
-        # TODO: parametrize with models when unittest dependency removed (new test case)
-        for model in (RidgeCV, LassoCV, LassoLarsCV, ElasticNetCV):
-            try:
-                AlphaSelection(model())
-            except YellowbrickTypeError:
-                pytest.fail("could not instantiate RegressorCV on alpha selection")
+    @pytest.mark.parametrize("model", [RidgeCV, LassoCV, LassoLarsCV, ElasticNetCV])
+    def test_regressor_cv(self, model):
+        """
+        Ensure "CV" regressors are allowed
+        """
+        try:
+            AlphaSelection(model())
+        except YellowbrickTypeError:
+            pytest.fail("could not instantiate RegressorCV on alpha selection")
 
-    def test_only_regressors(self):
+    @pytest.mark.parametrize("model", [SVC, KMeans, PCA])
+    def test_only_regressors(self, model):
         """
         Assert AlphaSelection only works with regressors
         """
-        # TODO: parameterize with classifier, clusterer, decomposition
         with pytest.raises(YellowbrickTypeError):
-            AlphaSelection(SVC())
+            AlphaSelection(model())
 
     def test_store_cv_values(self):
         """
@@ -99,21 +103,19 @@ def test_store_cv_values(self):
         model = AlphaSelection(RidgeCV(store_cv_values=False))
         assert model.estimator.store_cv_values
 
-    def test_get_alphas_param(self):
+    @pytest.mark.parametrize("model", [RidgeCV, LassoCV, ElasticNetCV])
+    def test_get_alphas_param(self, model):
         """
-        Assert that we can get the alphas from ridge, lasso, and elasticnet
+        Assert that we can get the alphas from original CV models
         """
         alphas = np.logspace(-10, -2, 100)
 
-        # Test original CV models
-        # TODO: parametrize this test with different models
-        for model in (RidgeCV, LassoCV, ElasticNetCV):
-            try:
-                model = AlphaSelection(model(alphas=alphas))
-                malphas = model._find_alphas_param()
-                assert_array_equal(alphas, malphas)
-            except YellowbrickValueError:
-                pytest.fail("could not find alphas on {}".format(model.name))
+        try:
+            model = AlphaSelection(model(alphas=alphas))
+            malphas = model._find_alphas_param()
+            assert_array_equal(alphas, malphas)
+        except YellowbrickValueError:
+            pytest.fail("could not find alphas on {}".format(model.name))
 
     def test_get_alphas_param_lassolars(self):
         """
@@ -128,24 +130,21 @@ def test_get_alphas_param_lassolars(self):
         except YellowbrickValueError:
             pytest.fail("could not find alphas on {}".format(model.name))
 
-    def test_get_errors_param(self):
+    @pytest.mark.parametrize("model", [RidgeCV, LassoCV, LassoLarsCV, ElasticNetCV])
+    def test_get_errors_param(self, model):
         """
         Test known models we can get the cv errors for alpha selection
         """
+        try:
+            model = AlphaSelection(model())
 
-        # Test original CV models
-        # TODO: parametrize this test with different models
-        for model in (RidgeCV, LassoCV, LassoLarsCV, ElasticNetCV):
-            try:
-                model = AlphaSelection(model())
-
-                X, y = make_regression()
-                model.fit(X, y)
+            X, y = make_regression()
+            model.fit(X, y)
 
-                errors = model._find_errors_param()
-                assert len(errors) > 0
-            except YellowbrickValueError:
-                pytest.fail("could not find errors on {}".format(model.name))
+            errors = model._find_errors_param()
+            assert len(errors) > 0
+        except YellowbrickValueError:
+            pytest.fail("could not find errors on {}".format(model.name))
 
     def test_score(self):
         """
diff --git a/tests/test_style/test_colors.py b/tests/test_style/test_colors.py
--- a/tests/test_style/test_colors.py
+++ b/tests/test_style/test_colors.py
@@ -235,7 +235,7 @@ def test_integrated_yb_colormap(self):
         )
         visualizer = SilhouetteVisualizer(KMeans(random_state=0), colormap='neural_paint')
         visualizer.fit(X)
-        visualizer.poof()
+        visualizer.finalize()
 
         tol = 3.2 if sys.platform == "win32" else 0.01  # Fails on AppVeyor with RMS 3.143
         self.assert_images_similar(visualizer, remove_legend=True, tol=tol)
diff --git a/tests/test_style/test_palettes.py b/tests/test_style/test_palettes.py
--- a/tests/test_style/test_palettes.py
+++ b/tests/test_style/test_palettes.py
@@ -17,7 +17,7 @@
 ## Imports
 ##########################################################################
 
-import unittest
+import pytest
 import numpy as np
 import matplotlib as mpl
 
@@ -35,7 +35,7 @@
 ## Color Palette Tests
 ##########################################################################
 
-class ColorPaletteObjectTests(VisualTestCase):
+class TestColorPaletteObject(VisualTestCase):
     """
     Tests the ColorPalette object
     """
@@ -54,11 +54,11 @@ def test_init_palette_by_name(self):
                     "Could not instantiate {} color palette by name".format(name)
                 )
 
-            self.assertEqual(value, palette)
+            assert value == palette
 
         # Try a name not in PALETTES
-        with self.assertRaises(YellowbrickValueError):
-            self.assertNotIn('foo', PALETTES, "Cannot test bad name 'foo' it is in PALETTES!")
+        with pytest.raises(YellowbrickValueError):
+            assert 'foo' not in PALETTES, "Cannot test bad name 'foo' it is in PALETTES!"
             palette = ColorPalette('foo')
 
     def test_init_palette_by_list(self):
@@ -69,12 +69,12 @@ def test_init_palette_by_list(self):
         # Try all the values in the palettes (HEX)
         for value in PALETTES.values():
             palette = ColorPalette(value)
-            self.assertEqual(len(value), len(palette))
+            assert len(value) == len(palette)
 
         # Try all the values converted to RGB
         for value in PALETTES.values():
             palette = ColorPalette(map(mpl.colors.colorConverter.to_rgb, value))
-            self.assertEqual(len(value), len(palette))
+            assert len(value) == len(palette)
 
     def test_color_palette_context(self):
         """
@@ -84,10 +84,10 @@ def test_color_palette_context(self):
         context = color_palette('dark')
 
         with ColorPalette('dark') as palette:
-            self.assertIsInstance(palette, ColorPalette)
-            self.assertEqual(get_color_cycle(), context)
+            assert isinstance(palette, ColorPalette)
+            assert get_color_cycle() == context
 
-        self.assertEqual(get_color_cycle(), default)
+        assert get_color_cycle() == default
 
     def test_as_hex_as_rgb(self):
         """
@@ -97,16 +97,16 @@ def test_as_hex_as_rgb(self):
         expected  = PALETTES['flatui']
         morgified = palette.as_hex()
 
-        self.assertIsNot(morgified, palette)
-        self.assertIsInstance(morgified, ColorPalette)
-        self.assertEqual(morgified, expected)
+        assert morgified is not palette
+        assert isinstance(morgified, ColorPalette)
+        assert morgified == expected
 
         remorgified = morgified.as_rgb()
-        self.assertIsNot(remorgified, morgified)
-        self.assertIsNot(remorgified, palette)
-        self.assertEqual(remorgified, palette)
+        assert remorgified is not morgified
+        assert remorgified is not palette
+        assert remorgified == palette
 
-    @unittest.skip("not implemented yet")
+    @pytest.mark.skip(reason="not implemented yet")
     def test_plot_color_palette(self):
         """
         Test the plotting of a color palette for color visualization
@@ -116,7 +116,7 @@ def test_plot_color_palette(self):
         )
 
 
-class ColorPaletteFunctionTests(VisualTestCase):
+class TestColorPaletteFunction(VisualTestCase):
     """
     Tests the color_palette function.
     """
@@ -127,7 +127,7 @@ def test_current_palette(self):
         """
         pal = color_palette(["red", "blue", "green"], 3)
         set_palette(pal, 3)
-        self.assertEqual(pal, get_color_cycle())
+        assert pal == get_color_cycle()
 
         # Reset the palette
         set_aesthetic()
@@ -141,9 +141,9 @@ def test_palette_context(self):
         context_pal = color_palette("muted")
 
         with color_palette(context_pal):
-            self.assertEqual(get_color_cycle(), context_pal)
+            assert get_color_cycle() == context_pal
 
-        self.assertEqual(get_color_cycle(), default_pal)
+        assert get_color_cycle() == default_pal
 
     def test_big_palette_context(self):
         """
@@ -155,9 +155,9 @@ def test_big_palette_context(self):
 
         set_palette(original_pal)
         with color_palette(context_pal, 10):
-            self.assertEqual(get_color_cycle(), context_pal)
+            assert get_color_cycle() == context_pal
 
-        self.assertEqual(get_color_cycle(), original_pal)
+        assert get_color_cycle() == original_pal
 
         # Reset default
         set_aesthetic()
@@ -169,7 +169,7 @@ def test_yellowbrick_palettes(self):
         pals = ["accent", "dark", "pastel", "bold", "muted"]
         for name in pals:
             pal_out = color_palette(name)
-            self.assertEqual(len(pal_out), 6, "{} is not of len 6".format(name))
+            assert len(pal_out) == 6, "{} is not of len 6".format(name)
 
     def test_seaborn_palettes(self):
         """
@@ -179,7 +179,7 @@ def test_seaborn_palettes(self):
                 "sns_bright", "sns_dark", "sns_colorblind"]
         for name in pals:
             pal_out = color_palette(name)
-            self.assertEqual(len(pal_out), 6)
+            assert len(pal_out) == 6
 
     def test_other_palettes(self):
         """
@@ -188,7 +188,8 @@ def test_other_palettes(self):
         pals = ["flatui", "paired", "neural_paint", "set1"]
         for name in pals:
             pal_out = color_palette(name)
-            self.assertTrue(pal_out)
+            assert pal_out is not None
+            assert len(pal_out) > 0
 
 
     def test_bad_palette_name(self):
@@ -196,10 +197,10 @@ def test_bad_palette_name(self):
         Test that a bad palette name raises an exception
         """
 
-        with self.assertRaises(ValueError):
+        with pytest.raises(ValueError):
             color_palette("IAmNotAPalette")
 
-        with self.assertRaises(YellowbrickValueError):
+        with pytest.raises(YellowbrickValueError):
             color_palette("IAmNotAPalette")
 
     def test_bad_palette_colors(self):
@@ -208,10 +209,10 @@ def test_bad_palette_colors(self):
         """
 
         pal = ["red", "blue", "iamnotacolor"]
-        with self.assertRaises(ValueError):
+        with pytest.raises(ValueError):
             color_palette(pal)
 
-        with self.assertRaises(YellowbrickValueError):
+        with pytest.raises(YellowbrickValueError):
             color_palette(pal)
 
     def test_palette_is_list_of_tuples(self):
@@ -222,10 +223,10 @@ def test_palette_is_list_of_tuples(self):
         pal_in = np.array(["red", "blue", "green"])
         pal_out = color_palette(pal_in, 3)
 
-        self.assertIsInstance(pal_out, list)
-        self.assertIsInstance(pal_out[0], tuple)
-        self.assertIsInstance(pal_out[0][0], float)
-        self.assertEqual(len(pal_out[0]), 3)
+        assert isinstance(pal_out, list)
+        assert isinstance(pal_out[0], tuple)
+        assert isinstance(pal_out[0][0], float)
+        assert len(pal_out[0]) == 3
 
     def test_palette_cycles(self):
         """
@@ -233,20 +234,20 @@ def test_palette_cycles(self):
         """
         accent = color_palette("accent")
         double_accent = color_palette("accent", 12)
-        self.assertEqual(double_accent, accent + accent)
+        assert double_accent == accent + accent
 
-    @unittest.skip("Discovered this commented out, don't know why")
+    @pytest.mark.skip(reason="discovered this commented out, don't know why")
     def test_cbrewer_qual(self):
         """
         Test colorbrewer qualitative palettes
         """
         pal_short = mpl_palette("Set1", 4)
         pal_long = mpl_palette("Set1", 6)
-        self.assertEqual(pal_short, pal_long[:4])
+        assert pal_short == pal_long[:4]
 
         pal_full = palettes.mpl_palette("Set2", 8)
         pal_long = palettes.mpl_palette("Set2", 10)
-        self.assertEqual(pal_full, pal_long[:8])
+        assert pal_full == pal_long[:8]
 
     def test_color_codes(self):
         """
@@ -257,7 +258,7 @@ def test_color_codes(self):
         for code, color in zip("bgrmyck", colors):
             rgb_want = mpl.colors.colorConverter.to_rgb(color)
             rgb_got = mpl.colors.colorConverter.to_rgb(code)
-            self.assertEqual(rgb_want, rgb_got)
+            assert rgb_want == rgb_got
         set_color_codes("reset")
 
     def test_as_hex(self):
@@ -266,10 +267,10 @@ def test_as_hex(self):
         """
         pal = color_palette("accent")
         for rgb, hex in zip(pal, pal.as_hex()):
-            self.assertEqual(mpl.colors.rgb2hex(rgb), hex)
+            assert mpl.colors.rgb2hex(rgb) == hex
 
         for rgb_e, rgb_v in zip(pal, pal.as_hex().as_rgb()):
-            self.assertEqual(rgb_e, rgb_v)
+            assert rgb_e == rgb_v
 
     def test_preserved_palette_length(self):
         """
@@ -277,7 +278,7 @@ def test_preserved_palette_length(self):
         """
         pal_in = color_palette("Set1", 10)
         pal_out = color_palette(pal_in)
-        self.assertEqual(pal_in, pal_out)
+        assert pal_in == pal_out
 
     def test_color_sequence(self):
         """
@@ -286,33 +287,30 @@ def test_color_sequence(self):
         for name, ncols in SEQUENCES.items():
             for n in ncols.keys():
                 cmap = color_sequence(name, n)
-                self.assertEqual(name, cmap.name)
-                self.assertEqual(n, cmap.N)
+                assert name == cmap.name
+                assert n == cmap.N
 
     def test_color_sequence_default(self):
         """
         Assert the default color sequence is RdBu
         """
         cmap = color_sequence()
-        self.assertEqual(cmap.name, "RdBu")
-        self.assertEqual(cmap.N, 11)
+        assert cmap.name == "RdBu"
+        assert cmap.N == 11
 
     def test_color_sequence_unrecocognized(self):
         """
         Test value errors for unrecognized sequences
         """
-        with self.assertRaises(YellowbrickValueError):
+        with pytest.raises(YellowbrickValueError):
             color_sequence('PepperBucks', 3)
 
     def test_color_sequence_bounds(self):
         """
         Test color sequence out of bounds value error
         """
-        with self.assertRaises(YellowbrickValueError):
+        with pytest.raises(YellowbrickValueError):
             color_sequence('RdBu', 18)
 
-        with self.assertRaises(YellowbrickValueError):
+        with pytest.raises(YellowbrickValueError):
             color_sequence('RdBu', 2)
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/test_style/test_rcmod.py b/tests/test_style/test_rcmod.py
--- a/tests/test_style/test_rcmod.py
+++ b/tests/test_style/test_rcmod.py
@@ -18,13 +18,12 @@
 ## Imports
 ##########################################################################
 
-import unittest
+import pytest
 import numpy as np
 import matplotlib as mpl
 import numpy.testing as npt
 import yellowbrick.style.rcmod as yb_rcmod
 
-from distutils.version import LooseVersion
 from tests.base import VisualTestCase
 
 
@@ -38,9 +37,9 @@ class RCParamTester(VisualTestCase):
     """
 
     excluded_params = {
-        "backend",  # This cannot be changed by manipulating rc
-        "svg.embed_char_paths",  # This param causes test issues and is deprecated anyway
-        "font.family", # breaks the visualtest case
+        "backend",               # This cannot be changed by manipulating rc
+        "svg.embed_char_paths",  # This param causes test issues and is deprecated
+        "font.family",           # breaks the visualtest case
     }
 
     def flatten_list(self, orig_list):
@@ -57,7 +56,7 @@ def assert_rc_params(self, params):
             elif isinstance(v, np.ndarray):
                 npt.assert_array_equal(mpl.rcParams[k], v)
             else:
-                self.assertEqual((k, mpl.rcParams[k]), (k, v))
+                assert (k, mpl.rcParams[k]) == (k, v)
 
 
 ##########################################################################
@@ -80,8 +79,8 @@ def test_rc_override(self):
 
         rc = {"axes.facecolor": "blue", "foo.notaparam": "bar"}
         out = yb_rcmod._axes_style("darkgrid", rc)
-        self.assertEqual(out["axes.facecolor"], "blue")
-        self.assertNotIn("foo.notaparam", out)
+        assert out["axes.facecolor"] == "blue"
+        assert "foo.notaparam" not in out
 
     def test_set_style(self):
         """
@@ -91,7 +90,7 @@ def test_set_style(self):
         yb_rcmod.set_style()
         self.assert_rc_params(style_dict)
 
-    @unittest.skip("This test doesn't make sense without multiple styles")
+    @pytest.mark.skip(reason="this test doesn't make sense without multiple styles")
     def test_style_context_manager(self):
 
         yb_rcmod.set_style("darkgrid")
@@ -112,25 +111,20 @@ def test_style_context_independence(self):
         """
         Assert context and style independence
         """
-        self.assertTrue(set(yb_rcmod._style_keys) ^ set(yb_rcmod._context_keys))
+        assert len(set(yb_rcmod._style_keys) ^ set(yb_rcmod._context_keys)) > 0
 
     def test_set_rc(self):
         """
         Test the ability to set the mpl configuration rc dict
         """
         yb_rcmod.set_aesthetic(rc={"lines.linewidth": 4})
-        self.assertEqual(mpl.rcParams["lines.linewidth"], 4)
+        assert mpl.rcParams["lines.linewidth"] == 4
         yb_rcmod.set_aesthetic()
 
     def test_reset_defaults(self):
         """
         Test the ability to reset to the mpl defaults
         """
-        # Changes to the rc parameters make this test hard to manage
-        # on older versions of matplotlib, so we'll skip it
-        if LooseVersion(mpl.__version__) < LooseVersion("1.3"):
-            raise self.SkipTest
-
         yb_rcmod.reset_defaults()
         self.assert_rc_params(mpl.rcParamsDefault)
         yb_rcmod.set_aesthetic()
@@ -139,12 +133,6 @@ def test_reset_orig(self):
         """
         Test the ability to reset to the original (respecting custom styles)
         """
-
-        # Changes to the rc parameters make this test hard to manage
-        # on older versions of matplotlib, so we'll skip it
-        if LooseVersion(mpl.__version__) < LooseVersion("1.3"):
-            raise self.SkipTest
-
         yb_rcmod.reset_orig()
         self.assert_rc_params(mpl.rcParamsOrig)
         yb_rcmod.set_aesthetic()
@@ -171,7 +159,7 @@ def test_font_scale(self):
                      "xtick.labelsize", "ytick.labelsize", "font.size"]
 
         for k in font_keys:
-            self.assertEqual(notebook_ref[k] * 2, notebook_big[k])
+            assert notebook_ref[k] * 2 == notebook_big[k]
 
     def test_rc_override(self):
         """
@@ -180,8 +168,8 @@ def test_rc_override(self):
         key, val = "grid.linewidth", 5
         rc = {key: val, "foo": "bar"}
         out = yb_rcmod._plotting_context("talk", rc=rc)
-        self.assertEqual(out[key], val)
-        self.assertNotIn("foo", out)
+        assert out[key] == val
+        assert "foo" not in out
 
     def test__set_context(self):
         """
@@ -191,7 +179,7 @@ def test__set_context(self):
         yb_rcmod._set_context()
         self.assert_rc_params(context_dict)
 
-    @unittest.skip("This test doesn't make sense without multiple contexts")
+    @pytest.mark.skip(reason="this test doesn't make sense without multiple contexts")
     def test_context_context_manager(self):
 
         yb_rcmod._set_context("notebook")
@@ -208,6 +196,3 @@ def func():
         func()
         self.assert_rc_params(orig_params)
 
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/test_target/test_binning.py b/tests/test_target/test_binning.py
--- a/tests/test_target/test_binning.py
+++ b/tests/test_target/test_binning.py
@@ -1,7 +1,7 @@
 # tests.test_target.test_binning
 # Tests for the BalancedBinningReference visualizer
 #
-# Author:   Juan L. Kehoe (juanluo2008@gmail.com)
+# Author:  Juan L. Kehoe (juanluo2008@gmail.com)
 # Author:  Prema Damodaran Roman (pdamo24@gmail.com)
 # Created: Thu Jul 20 10:21:49 2018 -0400
 #
@@ -11,29 +11,28 @@
 from tests.dataset import DatasetMixin
 from yellowbrick.target.binning import *
 
+
 ##########################################################################
 ## BalancedBinningReference Tests
 ##########################################################################
 
 class TestBalancedBinningReference(VisualTestCase, DatasetMixin):
-	"""
-	Test the BalancedBinningReference visualizer 
-	"""
-
-	def test_balancedbinningreference(self):
-		"""
-		Test Histogram on a real dataset
-		"""
-		# Load the data from the fixture
-		dataset = self.load_data('occupancy')
-
-		# Get the data
-		y = dataset["temperature"]
-
-		
-		visualizer = BalancedBinningReference()
-		visualizer.fit(y)
-		visualizer.finalize()
-		self.assert_images_similar(visualizer, tol=0.5)
-			
-		
\ No newline at end of file
+    """
+    Test the BalancedBinningReference visualizer
+    """
+
+    def test_balancedbinningreference(self):
+        """
+        Test Histogram on a real dataset
+        """
+        # Load the data from the fixture
+        dataset = self.load_data('occupancy')
+
+        # Get the data
+        y = dataset["temperature"]
+
+        visualizer = BalancedBinningReference()
+        visualizer.fit(y)
+        visualizer.finalize()
+        self.assert_images_similar(visualizer, tol=0.5)
+
diff --git a/tests/test_target/test_class_balance.py b/tests/test_target/test_class_balance.py
--- a/tests/test_target/test_class_balance.py
+++ b/tests/test_target/test_class_balance.py
@@ -66,7 +66,7 @@ def make_fixture(binary=False, balanced=False, split=False):
 ##  Tests
 ##########################################################################
 
-class ClassBalanceTests(VisualTestCase, DatasetMixin):
+class TestClassBalance(VisualTestCase, DatasetMixin):
     """
     Test ClassBalance visualizer
     """
diff --git a/tests/test_target/test_feature_correlation.py b/tests/test_target/test_feature_correlation.py
--- a/tests/test_target/test_feature_correlation.py
+++ b/tests/test_target/test_feature_correlation.py
@@ -20,10 +20,6 @@
 import sys
 import pytest
 import numpy as np
-try:
-    import pandas as pd
-except ImportError:
-    pd = None
 import numpy.testing as npt
 import matplotlib.pyplot as plt
 
@@ -31,9 +27,13 @@
 from yellowbrick.exceptions import YellowbrickValueError, YellowbrickWarning
 
 from sklearn import datasets
-
 from tests.base import VisualTestCase
 
+try:
+    import pandas as pd
+except ImportError:
+    pd = None
+
 
 ##########################################################################
 ## Feature Correlation Tests
diff --git a/tests/test_text/test_base.py b/tests/test_text/test_base.py
--- a/tests/test_text/test_base.py
+++ b/tests/test_text/test_base.py
@@ -17,8 +17,6 @@
 ## Imports
 ##########################################################################
 
-import unittest
-
 from yellowbrick.base import *
 from yellowbrick.text.base import *
 from sklearn.base import BaseEstimator, TransformerMixin
@@ -28,22 +26,13 @@
 ## TextVisualizer Base Tests
 ##########################################################################
 
-class TextVisualizerBaseTests(unittest.TestCase):
+class TestTextVisualizerBase(object):
 
     def test_subclass(self):
         """
-        Assert the text visualizer is subclassed correctly 
+        Assert the text visualizer is subclassed correctly
         """
         visualizer = TextVisualizer()
-        self.assertIsInstance(visualizer, TransformerMixin)
-        self.assertIsInstance(visualizer, BaseEstimator)
-        self.assertIsInstance(visualizer, Visualizer)
-
-    # def test_interface(self):
-    #     """
-    #     Test the feature visualizer interface
-    #     """
-    #
-    #     visualizer = TextVisualizer()
-    #     with self.assertRaises(NotImplementedError):
-    #         visualizer.poof()
+        assert isinstance(visualizer, TransformerMixin)
+        assert isinstance(visualizer, BaseEstimator)
+        assert isinstance(visualizer, Visualizer)
diff --git a/tests/test_text/test_dispersion.py b/tests/test_text/test_dispersion.py
--- a/tests/test_text/test_dispersion.py
+++ b/tests/test_text/test_dispersion.py
@@ -19,12 +19,13 @@
 ##########################################################################
 
 import pytest
+import matplotlib.pyplot as plt
 
 from yellowbrick.exceptions import YellowbrickValueError
 from yellowbrick.datasets import load_hobbies
 from yellowbrick.text.dispersion import *
 from tests.base import VisualTestCase
-import matplotlib.pyplot as plt
+
 
 ##########################################################################
 ## Data
@@ -36,7 +37,7 @@
 ## DispersionPlot Tests
 ##########################################################################
 
-class DispersionPlotTests(VisualTestCase):
+class TestDispersionPlot(VisualTestCase):
 
     def test_quick_method(self):
         """
@@ -107,7 +108,6 @@ def test_dispersion_plot_annotate_docs(self):
 
         self.assert_images_similar(visualizer, tol=25.5)
 
-
     def test_dispersion_plot_color_by_class(self):
         """
         Assert no errors occur during DispersionPlot integration
diff --git a/tests/test_text/test_freqdist.py b/tests/test_text/test_freqdist.py
--- a/tests/test_text/test_freqdist.py
+++ b/tests/test_text/test_freqdist.py
@@ -35,7 +35,7 @@
 ## FreqDist Tests
 ##########################################################################
 
-class FreqDistTests(VisualTestCase):
+class TestFreqDist(VisualTestCase):
 
     @pytest.mark.xfail(
         IS_WINDOWS_OR_CONDA,
diff --git a/tests/test_text/test_postag.py b/tests/test_text/test_postag.py
--- a/tests/test_text/test_postag.py
+++ b/tests/test_text/test_postag.py
@@ -37,6 +37,7 @@
 except ImportError:
     spacy = None
 
+
 ##########################################################################
 ## Data
 ##########################################################################
@@ -201,11 +202,11 @@ def test_frequency_mode(self):
         viz = postag(tagged_docs, ax=ax, frequency=True)
         viz.finalize()
         ax.grid(False)
-        
+
         # Sorted tags i.e predetermined order
         sorted_tags = ['noun', 'adjective', 'punctuation', 'verb', 'preposition',
-                       'determiner', 'adverb', 'conjunction', 'pronoun', 'wh- word', 
-                       'modal', 'infinitive', 'possessive', 'other', 'symbol', 
+                       'determiner', 'adverb', 'conjunction', 'pronoun', 'wh- word',
+                       'modal', 'infinitive', 'possessive', 'other', 'symbol',
                        'existential', 'digit', 'non-English', 'interjection', 'list']
         # Extract tick labels from the plot
         ticks_ax = [tick.get_text() for tick in ax.xaxis.get_ticklabels()]
@@ -284,10 +285,10 @@ def test_stack_mode(self):
         visualizer.ax.grid(False)
 
         self.assert_images_similar(ax=ax)
-        
+
     def test_stack_frequency_mode(self):
         """
-        Assert no errors occur when the visualizer is run on both stack and 
+        Assert no errors occur when the visualizer is run on both stack and
         frequency mode
         """
         check_nltk_data()
@@ -298,11 +299,11 @@ def test_stack_frequency_mode(self):
         visualizer = PosTagVisualizer(stack=True, frequency=True, ax=ax)
         visualizer.fit(tagged_docs, y=['a','b','c'])
         visualizer.ax.grid(False)
-        
+
         # Sorted tags i.e predetermined order
         sorted_tags = ['noun', 'adjective', 'punctuation', 'verb', 'preposition',
-                       'determiner', 'adverb', 'conjunction', 'pronoun', 'wh- word', 
-                       'modal', 'infinitive', 'possessive', 'other', 'symbol', 
+                       'determiner', 'adverb', 'conjunction', 'pronoun', 'wh- word',
+                       'modal', 'infinitive', 'possessive', 'other', 'symbol',
                        'existential', 'digit', 'non-English', 'interjection', 'list']
         # Extract tick labels from the plot
         ticks_ax = [tick.get_text() for tick in ax.xaxis.get_ticklabels()]
diff --git a/tests/test_text/test_tsne.py b/tests/test_text/test_tsne.py
--- a/tests/test_text/test_tsne.py
+++ b/tests/test_text/test_tsne.py
@@ -41,6 +41,7 @@
 
 corpus = load_hobbies()
 
+
 ##########################################################################
 ## TSNE Tests
 ##########################################################################
@@ -132,16 +133,16 @@ def test_custom_colors_tsne(self):
         """
         ## produce random data
         X, y = make_classification(n_samples=200, n_features=100,
-                               n_informative=20, n_redundant=10, 
+                               n_informative=20, n_redundant=10,
                                n_classes=5, random_state=42)
-        
+
         ## specify a list of custom colors >= n_classes
         purple_blues = ["indigo", "orchid", "plum", "navy", "purple", "blue"]
-        
+
         ## instantiate the visualizer and check that self.colors is correct
         purple_tsne = TSNEVisualizer(colors=purple_blues, random_state=87)
         assert purple_tsne.colors == purple_blues
-        
+
         ## fit the visualizer and check that self.color_values is as long as
         ## n_classes and is the first n_classes items in self.colors
         purple_tsne.fit(X,y)
diff --git a/tests/test_text/test_umap.py b/tests/test_text/test_umap.py
--- a/tests/test_text/test_umap.py
+++ b/tests/test_text/test_umap.py
@@ -51,6 +51,7 @@
 
 corpus = load_hobbies()
 
+
 ##########################################################################
 ## UMAP Tests
 ##########################################################################
@@ -143,16 +144,16 @@ def test_custom_colors_umap(self):
         """
         ## produce random data
         X, y = make_classification(n_samples=200, n_features=100,
-                               n_informative=20, n_redundant=10, 
+                               n_informative=20, n_redundant=10,
                                n_classes=5, random_state=42)
-        
+
         ## specify a list of custom colors >= n_classes
         purple_blues = ["indigo", "orchid", "plum", "navy", "purple", "blue"]
-        
+
         ## instantiate the visualizer and check that self.colors is correct
         purple_umap = UMAPVisualizer(colors=purple_blues, random_state=87)
         assert purple_umap.colors == purple_blues
-        
+
         ## fit the visualizer and check that self.color_values is as long as
         ## n_classes and is the first n_classes items in self.colors
         purple_umap.fit(X,y)
diff --git a/tests/test_utils/test_decorators.py b/tests/test_utils/test_decorators.py
--- a/tests/test_utils/test_decorators.py
+++ b/tests/test_utils/test_decorators.py
@@ -17,8 +17,6 @@
 ## Imports
 ##########################################################################
 
-import unittest
-
 from yellowbrick.utils.decorators import *
 
 
@@ -26,7 +24,7 @@
 ## Decorator Tests
 ##########################################################################
 
-class DecoratorTests(unittest.TestCase):
+class TestDecorators(object):
     """
     Tests for the decorator utilities.
     """
@@ -43,10 +41,9 @@ def foo(self):
                 return "bar"
 
         viz = Visualizer()
-        self.assertFalse(hasattr(viz, "_foo"))
-        self.assertEqual(viz.foo, "bar")
-        self.assertEqual(viz._foo, "bar")
-
+        assert not hasattr(viz, "_foo")
+        assert viz.foo == "bar"
+        assert viz._foo == "bar"
 
     def test_docutil(self):
         """
@@ -69,34 +66,18 @@ def undecorated(*args, **kwargs):
             pass
 
         # Test the undecorated string to protect from magic
-        self.assertEqual(
-            undecorated.__doc__.strip(), "This is an undecorated function string."
-        )
+        assert undecorated.__doc__.strip() == "This is an undecorated function string."
 
         # Decorate manually and test the newly decorated return function.
         decorated = docutil(Visualizer.__init__)(undecorated)
-        self.assertEqual(
-            decorated.__doc__.strip(), "This is the correct docstring."
-        )
+        assert decorated.__doc__.strip() == "This is the correct docstring."
 
         # Assert that decoration modifies the original function.
-        self.assertEqual(
-            undecorated.__doc__.strip(), "This is the correct docstring."
-        )
+        assert undecorated.__doc__.strip() == "This is the correct docstring."
 
         @docutil(Visualizer.__init__)
         def sugar(*args, **kwargs):
             pass
 
         # Assert that syntactic sugar works as expected.
-        self.assertEqual(
-            sugar.__doc__.strip(), "This is the correct docstring."
-        )
-
-
-##########################################################################
-## Execute Tests
-##########################################################################
-
-if __name__ == "__main__":
-    unittest.main()
+        assert sugar.__doc__.strip() == "This is the correct docstring."
diff --git a/tests/test_utils/test_helpers.py b/tests/test_utils/test_helpers.py
--- a/tests/test_utils/test_helpers.py
+++ b/tests/test_utils/test_helpers.py
@@ -81,7 +81,6 @@ def test_str_input(self):
 ## Numeric Function Tests
 ##########################################################################
 
-
 class TestNumericFunctions(object):
     """
     Numeric helper functions
diff --git a/tests/test_utils/test_kneed.py b/tests/test_utils/test_kneed.py
--- a/tests/test_utils/test_kneed.py
+++ b/tests/test_utils/test_kneed.py
@@ -2,22 +2,22 @@
 # A port of the tests for knee-point detection package, kneed.
 #
 # Author:   Kevin Arvai
-# Author:   Pradeep Singh 
+# Author:   Pradeep Singh
 # Created:  Mon Apr 23 01:29:18 2019 -0400
 #
 # Copyright (C) 2017 Kevin Arvai
 # All rights reserved.
-# Redistribution and use in source and binary forms, with or without modification, 
+# Redistribution and use in source and binary forms, with or without modification,
 # are permitted provided that the following conditions are met:
-# 
+#
 # 1. Redistributions of source code must retain the above copyright notice, this list
 # of conditions and the following disclaimer.
 #
-# 2. Redistributions in binary form must reproduce the above copyright notice, this 
-# list of conditions and the following disclaimer in the documentation and/or other 
+# 2. Redistributions in binary form must reproduce the above copyright notice, this
+# list of conditions and the following disclaimer in the documentation and/or other
 # materials provided with the distribution.
 #
-# 3. Neither the name of the copyright holder nor the names of its contributors may 
+# 3. Neither the name of the copyright holder nor the names of its contributors may
 # be used to endorse or promote products derived from this software without specific
 # prior written permission.
 #
@@ -26,7 +26,7 @@
 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
@@ -35,10 +35,11 @@
 # ID: test_kneed.py [] pswaldia@no-reply.github.com $
 
 """
-This package contains a port of the tests for knee-point detection package, kneed, by 
-Kevin Arvai and hosted at https://github.com/arvkevi/kneed. This port is maintained 
+This package contains a port of the tests for knee-point detection package, kneed, by
+Kevin Arvai and hosted at https://github.com/arvkevi/kneed. This port is maintained
 with permission by the Yellowbrick contributors.
 """
+
 import numpy as np
 from yellowbrick.utils.kneed import KneeLocator
 
@@ -50,28 +51,28 @@
 
 
 def test_concave_increasing():
-    """Tests that a correct knee point is detected in 
+    """Tests that a correct knee point is detected in
     curve having concave and increasing nature."""
     kn = KneeLocator(x, y_concave_inc, curve_nature='concave', curve_direction='increasing')
     assert kn.knee == 2
 
 
 def test_concave_decreasing():
-    """Tests that a correct knee point is detected in 
+    """Tests that a correct knee point is detected in
     curve having concave and decreasing nature."""
     kn = KneeLocator(x, y_concave_dec, curve_nature='concave', curve_direction='decreasing')
     assert kn.knee == 7
 
 
 def test_convex_increasing():
-    """Tests that a correct knee point is detected in 
+    """Tests that a correct knee point is detected in
     curve having convex and increasing nature."""
     kn = KneeLocator(x, y_convex_inc, curve_nature='convex', curve_direction='increasing')
     assert kn.knee == 7
 
 
 def test_convex_decreasing():
-    """Tests that a correct knee point is detected in 
+    """Tests that a correct knee point is detected in
     curve having convex and decreasing nature."""
     kn = KneeLocator(x, y_convex_dec, curve_nature='convex', curve_direction='decreasing')
     assert kn.knee == 2
diff --git a/tests/test_utils/test_timer.py b/tests/test_utils/test_timer.py
--- a/tests/test_utils/test_timer.py
+++ b/tests/test_utils/test_timer.py
@@ -19,6 +19,7 @@
 from unittest import mock
 from yellowbrick.utils.timer import *
 
+
 ##########################################################################
 ## Helper Function Tests
 ##########################################################################