Repository: enthought/chaco

Issue:
Move away from nose style tests
Many tests in the Chaco codebase use the nosetests way of writing tests (a single function `test_...`, with generic asserts). This makes it difficult to run the test suite with another test framework. It would be good (and a lot of tedious work) to standardize on the standard unittest way of laying out tests. 

```
$ rg "^def test"
tests_with_backend/highlight_tool_test_case.py
22:def test_highlight_on_log_plot():

tests_with_backend/create_2d_test_case.py
20:def test_bounds_2d_case():
41:def test_process_2d_bounds_cell_plot():
95:def test_process_2d_bounds_vertex_data():

tests/test_image_plot.py
120:def test_horizontal_top_left():
126:def test_horizontal_bottom_left():
133:def test_horizontal_top_right():
140:def test_horizontal_bottom_right():
147:def test_vertical_top_left():
153:def test_vertical_bottom_left():
161:def test_vertical_top_right():
169:def test_vertical_bottom_right():

tests/test_image_utils.py
21:def test_viewer_zoomed_into_single_pixel():
28:def test_viewer_at_corner_of_single_image():
44:def test_viewer_zoomed_into_four_pixel_intersection():
54:def test_viewer_at_corner_of_four_pixel_image():

tests/component_tests.py
4:def test_padding_init():
39:def test_padding_trait_default():

scales/tests/test_formatters.py
10:def test_strftimeEx_01():
16:def test_strftimeEx_02():
22:def test_strftimeEx_03():
30:def test_strftimeEx_04():
40:def test_strftimeEx_04():
49:def test_strftimeEx_05():
58:def test_strftimeEx_06():
67:def test_strftimeEx_07():
80:def test_time_formatter_01():

scales/tests/test_time_scale.py
76:def test_tfrac_years_01():
83:def test_tfrac_years_01_Alice_Springs():
91:def test_tfrac_years_01_Honolulu():
99:def test_tfrac_years_02():
106:def test_tfrac_years_02_Alice_Springs():
114:def test_tfrac_years_02_Honolulu():
124:def test_tfrac_days_01():
131:def test_tfrac_days_01_Alice_Springs():
139:def test_tfrac_days_01_Honolulu():
147:def test_tfrac_days_02():
154:def test_tfrac_days_02_Alice_Springs():
162:def test_tfrac_days_02_Honolulu():
170:def test_tfrac_hours_01():
177:def test_tfrac_hours_01_Alice_Springs():
185:def test_tfrac_hours_02():
192:def test_tfrac_hours_02_Alice_Springs():
200:def test_tfrac_hours_03():
207:def test_tfrac_hours_03_Alice_Springs():
215:def test_tfrac_hours_04():
222:def test_tfrac_hours_04_Alice_Springs():
230:def test_tfrac_hours_05():
237:def test_tfrac_hours_05_Alice_Springs():
245:def test_tfrac_minutes_01():
251:def test_tfrac_minutes_02():
257:def test_tfrac_seconds_01():
263:def test_tfrac_seconds_02():
269:def test_tfrac_milliseconds_01():
275:def test_tfrac_milliseconds_02():
281:def test_tfrac_milliseconds_03():
287:def test_tfrac_milliseconds_04():
294:def test_tfrac_milliseconds_05():
306:def test_trange_hours_01():
311:def test_trange_hours_01_Alice_Springs():
317:def test_trange_hours_01_Honolulu():
323:def test_trange_hours_02():
328:def test_trange_hours_02_Alice_Springs():
334:def test_trange_hours_02_Honolulu():
340:def test_trange_hours_03():
345:def test_trange_hours_03_Alice_Springs():
351:def test_trange_hours_03_Honolulu():
357:def test_trange_hours_04():
362:def test_trange_hours_Alice_Springs():
368:def test_trange_hours_04_Honolulu():
374:def test_trange_hours_05():
379:def test_trange_hours_06():
384:def test_trange_hours_07():
389:def test_trange_hours_07_Alice_Springs():
395:def test_trange_hours_07_Honolulu():
401:def test_trange_seconds_01():
405:def test_trange_seconds_02():
409:def test_trange_seconds_03():
413:def test_trange_milliseconds_01():
417:def test_trange_milliseconds_02():
428:def test_time_scale_seconds_01():
433:def test_time_scale_seconds_02():
438:def test_time_scale_milliseconds_01():
444:def test_time_scale_with_formatter():
459:def test_calendar_scale_system_01():
```


Discussion:

Resolving patch:
diff --git a/ci/edmtool.py b/ci/edmtool.py
--- a/ci/edmtool.py
+++ b/ci/edmtool.py
@@ -80,7 +80,6 @@
 
 dependencies = {
     "six",
-    "nose",
     "mock",
     "numpy",
     "pandas",
@@ -144,8 +143,8 @@ def test(runtime, toolkit, environment):
     parameters = get_parameters(runtime, toolkit, environment)
     environ = environment_vars.get(toolkit, {}).copy()
     environ['PYTHONUNBUFFERED'] = "1"
-    commands_nobackend = [
-        "edm run -e {environment} -- coverage run -m nose.core chaco -v "
+    commands = [
+        "edm run -e {environment} -- coverage run -m unittest discover -v chaco"
     ]
 
     cwd = os.getcwd()
@@ -157,15 +156,7 @@ def test(runtime, toolkit, environment):
     click.echo("Running tests in '{environment}'".format(**parameters))
     with do_in_tempdir(files=['.coveragerc'], capture_files=['./.coverage*']):
         os.environ.update(environ)
-        execute(commands_nobackend, parameters)
-
-        if toolkit != 'null':
-            backend_tests = os.path.join(cwd, 'chaco/tests_with_backend')
-            commands_backend = [
-                ("edm run -e {{environment}} -- coverage run -a "
-                 "-m nose.core -v {}").format(backend_tests)
-            ]
-            execute(commands_backend, parameters)
+        execute(commands, parameters)
 
     click.echo('Done test')
 


Test patch:
diff --git a/chaco/scales/tests/test_formatters.py b/chaco/scales/tests/test_formatters.py
--- a/chaco/scales/tests/test_formatters.py
+++ b/chaco/scales/tests/test_formatters.py
@@ -1,4 +1,4 @@
-from __future__ import print_function
+import unittest
 
 from chaco.scales.formatters import strftimeEx, TimeFormatter
 
@@ -7,80 +7,79 @@
 # strftimeEx tests
 #----------------------------------------------------------------
 
-def test_strftimeEx_01():
-    t = 0.123
-    fmt = "%(ms)"
-    result = strftimeEx(fmt, t)
-    assert result == "123"
+class TestStrftimeEx(unittest.TestCase):
 
-def test_strftimeEx_02():
-    t = 0.123456
-    fmt = "%(us)"
-    result = strftimeEx(fmt, t)
-    assert result == "456"
+    def test_strftimeEx_01(self):
+        t = 0.123
+        fmt = "%(ms)"
+        result = strftimeEx(fmt, t)
+        self.assertEqual(result, "123")
 
-def test_strftimeEx_03():
-    t = 0.678910
-    fmt = "%(ms)"
-    # According to the code, the number that replaces (ms) is *rounded*,
-    # so this formt should give "679".
-    result = strftimeEx(fmt, t)
-    assert result == "679"
+    def test_strftimeEx_02(self):
+        t = 0.123456
+        fmt = "%(us)"
+        result = strftimeEx(fmt, t)
+        self.assertEqual(result, "456")
 
-def test_strftimeEx_04():
-    t = 0.678910
-    fmt = "%(ms).%(us)ms"
-    # According to the code, the number that replaces (ms) is *rounded*,
-    # so this formt should give "679.910ms".  (See the next test case for the
-    # correct way to do this.)
-    result = strftimeEx(fmt, t)
-    expected = "679.910ms"
-    assert result == expected
+    def test_strftimeEx_03(self):
+        t = 0.678910
+        fmt = "%(ms)"
+        # According to the code, the number that replaces (ms) is *rounded*,
+        # so this formt should give "679".
+        result = strftimeEx(fmt, t)
+        self.assertEqual(result, "679")
 
-def test_strftimeEx_04():
-    t = 0.678910
-    fmt = "%(ms_).%(us)ms"
-    # The format "%(ms_)" uses floor().
-    result = strftimeEx(fmt, t)
-    expected = "678.910ms"
-    print('result = "%s"  expected = "%s"' % (result, expected))
-    assert result == expected
+    def test_strftimeEx_04(self):
+        t = 0.678910
+        fmt = "%(ms).%(us)ms"
+        # According to the code, the number that replaces (ms) is *rounded*,
+        # so this formt should give "679.910ms".  (See the next test case for the
+        # correct way to do this.)
+        result = strftimeEx(fmt, t)
+        expected = "679.910ms"
+        self.assertEqual(result, expected)
 
-def test_strftimeEx_05():
-    """Test rounding that affects the seconds."""
-    t = 7.9999999
-    fmt = "%S %(ms_) %(us)"
-    result = strftimeEx(fmt, t)
-    expected = "08 000 000"
-    print('result = "%s"  expected = "%s"' % (result, expected))
-    assert result == expected
+    def test_strftimeEx_04(self):
+        t = 0.678910
+        fmt = "%(ms_).%(us)ms"
+        # The format "%(ms_)" uses floor().
+        result = strftimeEx(fmt, t)
+        expected = "678.910ms"
+        self.assertEqual(result, expected)
 
-def test_strftimeEx_06():
-    """Test rounding that affects the seconds."""
-    t = 7.9996
-    fmt = "%S %(ms)"
-    result = strftimeEx(fmt, t)
-    expected = "08 000"
-    print('result = "%s"  expected = "%s"' % (result, expected))
-    assert result == expected
+    def test_strftimeEx_05(self):
+        # Test rounding that affects the seconds.
+        t = 7.9999999
+        fmt = "%S %(ms_) %(us)"
+        result = strftimeEx(fmt, t)
+        expected = "08 000 000"
+        self.assertEqual(result, expected)
 
-def test_strftimeEx_07():
-    """Test rounding that affects the seconds."""
-    t = 7.9996
-    fmt = "%S %(ms_)"
-    result = strftimeEx(fmt, t)
-    expected = "07 999"
-    print('result = "%s"  expected = "%s"' % (result, expected))
-    assert result == expected
+    def test_strftimeEx_06(self):
+        # Test rounding that affects the seconds.
+        t = 7.9996
+        fmt = "%S %(ms)"
+        result = strftimeEx(fmt, t)
+        expected = "08 000"
+        self.assertEqual(result, expected)
+
+    def test_strftimeEx_07(self):
+        # Test rounding that affects the seconds.
+        t = 7.9996
+        fmt = "%S %(ms_)"
+        result = strftimeEx(fmt, t)
+        expected = "07 999"
+        self.assertEqual(result, expected)
 
 #----------------------------------------------------------------
 # TimeFormatter tests
 #----------------------------------------------------------------
 
-def test_time_formatter_01():
-    tf = TimeFormatter()
-    ticks = [10.005, 10.0053, 10.0056]
-    labels = tf.format(ticks, char_width=130)
-    expected = ["5.000ms", "5.300ms", "5.600ms"]
-    print("labels =", labels, " expected =", expected)
-    assert labels == expected
+class TestTimeFormatter(unittest.TestCase):
+
+    def test_time_formatter_01(self):
+        tf = TimeFormatter()
+        ticks = [10.005, 10.0053, 10.0056]
+        labels = tf.format(ticks, char_width=130)
+        expected = ["5.000ms", "5.300ms", "5.600ms"]
+        self.assertEqual(labels, expected)
diff --git a/chaco/scales/scales_test_case.py b/chaco/scales/tests/test_scales.py
similarity index 88%
rename from chaco/scales/scales_test_case.py
rename to chaco/scales/tests/test_scales.py
--- a/chaco/scales/scales_test_case.py
+++ b/chaco/scales/tests/test_scales.py
@@ -1,11 +1,9 @@
-from __future__ import print_function
-
 from traits.testing.unittest_tools import unittest
 
 from numpy import array
 
-from .formatters import BasicFormatter, OffsetFormatter
-from .scales import Pow10Scale, FixedScale, LogScale, DefaultScale, ScaleSystem, frange
+from ..formatters import BasicFormatter, OffsetFormatter
+from ..scales import Pow10Scale, FixedScale, LogScale, DefaultScale, ScaleSystem, frange
 
 
 class TicksTestCase(unittest.TestCase):
@@ -148,9 +146,7 @@ def test2_nice_sci(self):
             val = lst[0]
             for mdigits, desired in lst[1:]:
                 s = fmt._nice_sci(val, mdigits)
-                if s != desired:
-                    print("Mismatch for", val, "; desired:", desired, "actual:", s)
-
+                self.assertEqual(s, desired)
 
     def test_estimate_default_scale(self):
         fmt = BasicFormatter()
@@ -177,12 +173,18 @@ def test_width_based_default_scale(self):
         test_intervals = ((1, 100, 80),
                           (1, 100, 40),
                           (1, 100, 20),)
-        print()
-        for start, end, width in test_intervals:
+        res0 = [
+            (10.0, '10'), (20.0, '20'), (30.0, '30'), (40.0, '40'),
+            (50.0, '50'), (60.0, '60'), (70.0, '70'), (80.0, '80'),
+            (90.0, '90'), (100.0, '100')
+        ]
+        res1 = [(25.0, '25'), (50.0, '50'), (75.0, '75'), (100.0, '100')]
+        res2 = [(100.0, '100')]
+        all_expected = [res0, res1, res2]
+
+        for (start, end, width), expected in zip(test_intervals, all_expected):
             labels = scale.labels(start, end, char_width=width)
-            print("(%d,%d)" % (start,end), " avail:", width, end=" ")
-            print(" used:", sum([len(x[1]) for x in labels]))
-        return
+            self.assertEqual(labels, expected)
 
     def test_scale_system(self):
         scale = ScaleSystem(FixedScale(resolution = 1.0),
@@ -200,16 +202,14 @@ def test_scale_system(self):
                           (1, 10, 100),
                           (1, 10, 50),
                           (1, 10, 20),)
-        print()
-        for start, end, width in test_intervals:
+        expected_lengths = [40, 10, 5, 5, 1, 10, 10, 4]
+
+        for (start, end, width), ll in zip(test_intervals, expected_lengths):
             labels = scale.labels(start, end, char_width=width)
-            print("(%d,%d)" % (start,end), " avail:", width, end=" ")
-            print(" used:", sum([len(x[1]) for x in labels]), end=" ")
-            print(list(zip(*labels))[1])
-        return
+            self.assertEqual(len(labels), ll)
 
-class OffsetFormatterTestCase(TicksTestCase):
 
+class OffsetFormatterTestCase(TicksTestCase):
 
     def test_format(self):
 
@@ -226,14 +226,5 @@ def test_format(self):
             scale = FixedScale(resolution = resol)
             numlabels = 12
             ticks = scale.ticks(start, end, numlabels)
-            print("range:", start, end)
             labels = fmt.format(ticks, numlabels, None)
-            print("Labels:", labels)
-            print("estimated width:", fmt.estimate_width(start, end, numlabels))
-            print("actual width:", sum(map(len, labels)))
-
-
-
-if __name__ == "__main__":
-    import nose
-    nose.run()
+            self.assertEqual(len(ticks), len(labels))
diff --git a/chaco/scales/tests/test_time_scale.py b/chaco/scales/tests/test_time_scale.py
--- a/chaco/scales/tests/test_time_scale.py
+++ b/chaco/scales/tests/test_time_scale.py
@@ -1,12 +1,10 @@
-
-
 import datetime
 import os
 import contextlib
-
-import six.moves as sm
+import unittest
 
 import numpy as np
+import numpy.testing as nptest
 
 from chaco.scales.time_scale import (
     tfrac, trange, TimeScale, CalendarScaleSystem)
@@ -73,350 +71,355 @@ def set_timezone(tz):
 # tfrac tests
 #----------------------------------------------------------------
 
-def test_tfrac_years_01():
-    with set_timezone(UTC):
+class TestTFrac(unittest.TestCase):
+
+    def test_tfrac_years_01(self):
+        with set_timezone(UTC):
+            t = 3601
+            (base, frac) = tfrac(t, years=1)
+            self.assertEqual(base, 0)
+            self.assertEqual(frac, 3601)
+
+    def test_tfrac_years_01_Alice_Springs(self):
+        # Australia/North (UTC+09:30, never DST)
+        with set_timezone(ALICE_SPRINGS):
+            t = 3601
+            (base, frac) = tfrac(t, years=1)
+            self.assertEqual(base, 3600 * -9.5)  # Alice Springs year start UTC timestamp
+            self.assertEqual(frac, 3600 * 10.5 + 1)  # 10:30:01 in the morning Jan 1
+
+    def test_tfrac_years_01_Honolulu(self):
+        # Pacific/Honolulu (UTC-10:00, never DST)
+        with set_timezone(HONOLULU):
+            t = 3601
+            (base, frac) = tfrac(t, years=1)
+            self.assertEqual(base, 3600 * (-365*24 + 10))  # previous Honolulu year start UTC timestamp
+            self.assertEqual(frac, 3600 * (364*24 + 15) + 1)  # 15:00:01 in the afternoon, Dec 31
+
+    def test_tfrac_years_02(self):
+        with set_timezone(UTC):
+            t = 3601
+            (base, frac) = tfrac(t, years=10)
+            self.assertEqual(base, 0)
+            self.assertEqual(frac, 3601)
+
+    def test_tfrac_years_02_Alice_Springs(self):
+        # Australia/North (UTC+09:30, never DST)
+        with set_timezone(ALICE_SPRINGS):
+            t = 3601
+            (base, frac) = tfrac(t, years=10)
+            self.assertEqual(base, 3600 * -9.5)  # Alice Springs decade start UTC timestamp
+            self.assertEqual(frac, 3600 * 10.5 + 1)  # 10:30:01 in the morning Jan 1
+
+    def test_tfrac_years_02_Honolulu(self):
+        # Pacific/Honolulu (UTC-10:00, never DST)
+        with set_timezone(HONOLULU):
+            t = 3601
+            (base, frac) = tfrac(t, years=10)
+            # previous Honolulu decade start UTC timestamp (including leap years)
+            self.assertEqual(base, 3600 * (-(365*10 + 3)*24 + 10))
+            # 15:00:01 in the afternoon, Dec 31, 9 years into decade
+            self.assertEqual(frac, 3600 * ((365*9+3 + 364)*24 + 15) + 1)
+
+    def test_tfrac_days_01(self):
+        with set_timezone(UTC):
+            t = 3601
+            (base, frac) = tfrac(t, days=1)
+            self.assertEqual(base, 0)
+            self.assertEqual(frac, 3601)
+
+    def test_tfrac_days_01_Alice_Springs(self):
+        # Australia/North (UTC+09:30, never DST)
+        with set_timezone(ALICE_SPRINGS):
+            t = 3601
+            (base, frac) = tfrac(t, days=1)
+            self.assertEqual(base, 3600 * -9.5)  # Alice Springs day start UTC timestamp
+            self.assertEqual(frac, 3600 * 10.5 + 1)  # 10:30:01 in the morning
+
+    def test_tfrac_days_01_Honolulu(self):
+        # Pacific/Honolulu (UTC-10:00, never DST)
+        with set_timezone(HONOLULU):
+            t = 3601
+            (base, frac) = tfrac(t, days=1)
+            self.assertEqual(base, 3600 * (-24 + 10))  # previous Honolulu day start UTC timestamp
+            self.assertEqual(frac, 3600 * 15 + 1)  # 15:00:01 in the afternoon
+
+    def test_tfrac_days_02(self):
+        with set_timezone(UTC):
+            t = 3*24.0*3600 + 1000.0
+            (base, frac) = tfrac(t, days=1)
+            self.assertEqual(base, 3600 * 24 * 3)
+            self.assertEqual(frac, 1000)
+
+    def test_tfrac_days_02_Alice_Springs(self):
+        # Australia/North (UTC+09:30, never DST)
+        with set_timezone(ALICE_SPRINGS):
+            t = 3*24.0*3600 + 1000.0
+            (base, frac) = tfrac(t, days=1)
+            self.assertEqual(base, 3600 * (24 * 3 - 9.5))
+            self.assertEqual(frac, 3600 * 9.5 + 1000)
+
+    def test_tfrac_days_02_Honolulu(self):
+        # Pacific/Honolulu (UTC-10:00, never DST)
+        with set_timezone(HONOLULU):
+            t = 3*24.0*3600 + 1000.0
+            (base, frac) = tfrac(t, days=1)
+            self.assertEqual(base, 3600 * (24 * 2 + 10))
+            self.assertEqual(frac, 3600 * (24 - 10) + 1000)
+
+    def test_tfrac_hours_01(self):
+        with set_timezone(UTC):
+            t = 3601
+            (base, frac) = tfrac(t, hours=1)
+            self.assertEqual(base, 3600)
+            self.assertEqual(frac, 1)
+
+    def test_tfrac_hours_01_Alice_Springs(self):
+        # Australia/North (UTC+09:30, never DST)
+        with set_timezone(ALICE_SPRINGS):
+            t = 3601
+            (base, frac) = tfrac(t, hours=1)
+            self.assertEqual(base, 1800)
+            self.assertEqual(frac, 1801)
+
+    def test_tfrac_hours_02(self):
+        with set_timezone(UTC):
+            t = 3601
+            (base, frac) = tfrac(t, hours=2)
+            self.assertEqual(base, 0)
+            self.assertEqual(frac, 3601)
+
+    def test_tfrac_hours_02_Alice_Springs(self):
+        # Australia/North (UTC+09:30, never DST)
+        with set_timezone(ALICE_SPRINGS):
+            t = 3601
+            (base, frac) = tfrac(t, hours=2)
+            self.assertEqual(base, 1800)
+            self.assertEqual(frac, 1801)
+
+    def test_tfrac_hours_03(self):
+        with set_timezone(UTC):
+            t = 3600 * 5.5
+            (base, frac) = tfrac(t, hours=2)
+            self.assertEqual(base, 3600 * 4)
+            self.assertEqual(frac, 3600 * 1.5)
+
+    def test_tfrac_hours_03_Alice_Springs(self):
+        # Australia/North (UTC+09:30, never DST)
+        with set_timezone(ALICE_SPRINGS):
+            t = 3600 * 5.5
+            (base, frac) = tfrac(t, hours=2)
+            self.assertEqual(base, 3600 * 4.5)
+            self.assertEqual(frac, 3600 * 1)
+
+    def test_tfrac_hours_04(self):
+        with set_timezone(UTC):
+            t = 3600 * 5.5
+            (base, frac) = tfrac(t, hours=3)
+            self.assertEqual(base, 3600 * 3.0)
+            self.assertEqual(frac, 3600 * 2.5)
+
+    def test_tfrac_hours_04_Alice_Springs(self):
+        # Australia/North (UTC+09:30, never DST)
+        with set_timezone(ALICE_SPRINGS):
+            t = 3600 * 5.5
+            (base, frac) = tfrac(t, hours=3)
+            self.assertEqual(base, 3600 * 5.5)
+            self.assertEqual(frac, 3600 * 0)
+
+    def test_tfrac_hours_05(self):
+        with set_timezone(UTC):
+            t = 3600 * 15.5
+            (base, frac) = tfrac(t, hours=6)
+            self.assertEqual(base, 3600 * 12.0)
+            self.assertEqual(frac, 3600 *  3.5)
+
+    def test_tfrac_hours_05_Alice_Springs(self):
+        # Australia/North (UTC+09:30, never DST)
+        with set_timezone(ALICE_SPRINGS):
+            t = 3600 * 15.5
+            (base, frac) = tfrac(t, hours=6)
+            self.assertEqual(base, 3600 * 14.5)
+            self.assertEqual(frac, 3600 *  1.0)
+
+    def test_tfrac_minutes_01(self):
         t = 3601
-        (base, frac) = tfrac(t, years=1)
-        assert base == 0
-        assert frac == 3601
+        (base, frac) = tfrac(t, minutes=1)
+        self.assertEqual(base, 3600)
+        self.assertEqual(frac, 1)
 
-def test_tfrac_years_01_Alice_Springs():
-    # Australia/North (UTC+09:30, never DST)
-    with set_timezone(ALICE_SPRINGS):
-        t = 3601
-        (base, frac) = tfrac(t, years=1)
-        assert base == 3600 * -9.5  # Alice Springs year start UTC timestamp
-        assert frac == 3600 * 10.5 + 1  # 10:30:01 in the morning Jan 1
+    def test_tfrac_minutes_02(self):
+        t = 123.5
+        (base, frac) = tfrac(t, minutes=1)
+        self.assertEqual(base, 120)
+        self.assertEqual(frac, 3.5)
 
-def test_tfrac_years_01_Honolulu():
-    # Pacific/Honolulu (UTC-10:00, never DST)
-    with set_timezone(HONOLULU):
+    def test_tfrac_seconds_01(self):
         t = 3601
-        (base, frac) = tfrac(t, years=1)
-        assert base == 3600 * (-365*24 + 10)  # previous Honolulu year start UTC timestamp
-        assert frac == 3600 * (364*24 + 15) + 1  # 15:00:01 in the afternoon, Dec 31
-
-def test_tfrac_years_02():
-    with set_timezone(UTC):
-        t = 3601
-        (base, frac) = tfrac(t, years=10)
-        assert base == 0
-        assert frac == 3601
-
-def test_tfrac_years_02_Alice_Springs():
-    # Australia/North (UTC+09:30, never DST)
-    with set_timezone(ALICE_SPRINGS):
-        t = 3601
-        (base, frac) = tfrac(t, years=10)
-        assert base == 3600 * -9.5  # Alice Springs decade start UTC timestamp
-        assert frac == 3600 * 10.5 + 1  # 10:30:01 in the morning Jan 1
-
-def test_tfrac_years_02_Honolulu():
-    # Pacific/Honolulu (UTC-10:00, never DST)
-    with set_timezone(HONOLULU):
-        t = 3601
-        (base, frac) = tfrac(t, years=10)
-        # previous Honolulu decade start UTC timestamp (including leap years)
-        assert base == 3600 * (-(365*10 + 3)*24 + 10)
-        # 15:00:01 in the afternoon, Dec 31, 9 years into decade
-        assert frac == 3600 * ((365*9+3 + 364)*24 + 15) + 1
-
-def test_tfrac_days_01():
-    with set_timezone(UTC):
-        t = 3601
-        (base, frac) = tfrac(t, days=1)
-        assert base == 0
-        assert frac == 3601
-
-def test_tfrac_days_01_Alice_Springs():
-    # Australia/North (UTC+09:30, never DST)
-    with set_timezone(ALICE_SPRINGS):
-        t = 3601
-        (base, frac) = tfrac(t, days=1)
-        assert base == 3600 * -9.5  # Alice Springs day start UTC timestamp
-        assert frac == 3600 * 10.5 + 1  # 10:30:01 in the morning
-
-def test_tfrac_days_01_Honolulu():
-    # Pacific/Honolulu (UTC-10:00, never DST)
-    with set_timezone(HONOLULU):
-        t = 3601
-        (base, frac) = tfrac(t, days=1)
-        assert base == 3600 * (-24 + 10)  # previous Honolulu day start UTC timestamp
-        assert frac == 3600 * 15 + 1  # 15:00:01 in the afternoon
-
-def test_tfrac_days_02():
-    with set_timezone(UTC):
-        t = 3*24.0*3600 + 1000.0
-        (base, frac) = tfrac(t, days=1)
-        assert base == 3600 * 24 * 3
-        assert frac == 1000
-
-def test_tfrac_days_02_Alice_Springs():
-    # Australia/North (UTC+09:30, never DST)
-    with set_timezone(ALICE_SPRINGS):
-        t = 3*24.0*3600 + 1000.0
-        (base, frac) = tfrac(t, days=1)
-        assert base == 3600 * (24 * 3 - 9.5)
-        assert frac == 3600 * 9.5 + 1000
-
-def test_tfrac_days_02_Honolulu():
-    # Pacific/Honolulu (UTC-10:00, never DST)
-    with set_timezone(HONOLULU):
-        t = 3*24.0*3600 + 1000.0
-        (base, frac) = tfrac(t, days=1)
-        assert base == 3600 * (24 * 2 + 10)
-        assert frac == 3600 * (24 - 10) + 1000
-
-def test_tfrac_hours_01():
-    with set_timezone(UTC):
-        t = 3601
-        (base, frac) = tfrac(t, hours=1)
-        assert base == 3600
-        assert frac == 1
-
-def test_tfrac_hours_01_Alice_Springs():
-    # Australia/North (UTC+09:30, never DST)
-    with set_timezone(ALICE_SPRINGS):
-        t = 3601
-        (base, frac) = tfrac(t, hours=1)
-        assert base == 1800
-        assert frac == 1801
-
-def test_tfrac_hours_02():
-    with set_timezone(UTC):
-        t = 3601
-        (base, frac) = tfrac(t, hours=2)
-        assert base == 0
-        assert frac == 3601
-
-def test_tfrac_hours_02_Alice_Springs():
-    # Australia/North (UTC+09:30, never DST)
-    with set_timezone(ALICE_SPRINGS):
-        t = 3601
-        (base, frac) = tfrac(t, hours=2)
-        assert base == 1800
-        assert frac == 1801
-
-def test_tfrac_hours_03():
-    with set_timezone(UTC):
-        t = 3600 * 5.5
-        (base, frac) = tfrac(t, hours=2)
-        assert base == 3600 * 4
-        assert frac == 3600 * 1.5
-
-def test_tfrac_hours_03_Alice_Springs():
-    # Australia/North (UTC+09:30, never DST)
-    with set_timezone(ALICE_SPRINGS):
-        t = 3600 * 5.5
-        (base, frac) = tfrac(t, hours=2)
-        assert base == 3600 * 4.5
-        assert frac == 3600 * 1
-
-def test_tfrac_hours_04():
-    with set_timezone(UTC):
-        t = 3600 * 5.5
-        (base, frac) = tfrac(t, hours=3)
-        assert base == 3600 * 3.0
-        assert frac == 3600 * 2.5
-
-def test_tfrac_hours_04_Alice_Springs():
-    # Australia/North (UTC+09:30, never DST)
-    with set_timezone(ALICE_SPRINGS):
-        t = 3600 * 5.5
-        (base, frac) = tfrac(t, hours=3)
-        assert base == 3600 * 5.5
-        assert frac == 3600 * 0
-
-def test_tfrac_hours_05():
-    with set_timezone(UTC):
-        t = 3600 * 15.5
-        (base, frac) = tfrac(t, hours=6)
-        assert base == 3600 * 12.0
-        assert frac == 3600 *  3.5
-
-def test_tfrac_hours_05_Alice_Springs():
-    # Australia/North (UTC+09:30, never DST)
-    with set_timezone(ALICE_SPRINGS):
-        t = 3600 * 15.5
-        (base, frac) = tfrac(t, hours=6)
-        assert base == 3600 * 14.5
-        assert frac == 3600 *  1.0
-
-def test_tfrac_minutes_01():
-    t = 3601
-    (base, frac) = tfrac(t, minutes=1)
-    assert base == 3600
-    assert frac == 1
-
-def test_tfrac_minutes_02():
-    t = 123.5
-    (base, frac) = tfrac(t, minutes=1)
-    assert base == 120
-    assert frac == 3.5
-
-def test_tfrac_seconds_01():
-    t = 3601
-    (base, frac) = tfrac(t, seconds=1)
-    assert base == 3601
-    assert frac == 0
-
-def test_tfrac_seconds_02():
-    t = 1.75
-    (base, frac) = tfrac(t, seconds=1)
-    assert base == 1
-    assert frac == 0.75
-
-def test_tfrac_milliseconds_01():
-    t = 123.5
-    (base, frac) = tfrac(t, milliseconds=1)
-    assert base == 123.5
-    assert frac == 0.0
-
-def test_tfrac_milliseconds_02():
-    t = 10.0625
-    (base, frac) = tfrac(t, milliseconds=1)
-    assert base == 10.062
-    assert frac ==  0.0005
-
-def test_tfrac_milliseconds_03():
-    t = 10.0625
-    (base, frac) = tfrac(t, milliseconds=10)
-    assert base == 10.06
-    assert frac ==  0.0025
-
-def test_tfrac_milliseconds_04():
-    t = 1.0078121
-    # Note that the last digit is lost due to rounding to microsecond scale.
-    (base, frac) = tfrac(t, milliseconds=1)
-    assert base == 1.007
-    assert frac == 0.000812
-
-def test_tfrac_milliseconds_05():
-    t = 1.0078056
-    # Note that the last digit is lost due to rounding to microsecond scale.
-    (base, frac) = tfrac(t, milliseconds=1)
-    assert base == 1.007
-    assert frac == 0.000806
+        (base, frac) = tfrac(t, seconds=1)
+        self.assertEqual(base, 3601)
+        self.assertEqual(frac, 0)
+
+    def test_tfrac_seconds_02(self):
+        t = 1.75
+        (base, frac) = tfrac(t, seconds=1)
+        self.assertEqual(base, 1)
+        self.assertEqual(frac, 0.75)
+
+    def test_tfrac_milliseconds_01(self):
+        t = 123.5
+        (base, frac) = tfrac(t, milliseconds=1)
+        self.assertEqual(base, 123.5)
+        self.assertEqual(frac, 0.0)
+
+    def test_tfrac_milliseconds_02(self):
+        t = 10.0625
+        (base, frac) = tfrac(t, milliseconds=1)
+        self.assertEqual(base, 10.062)
+        self.assertEqual(frac,  0.0005)
+
+    def test_tfrac_milliseconds_03(self):
+        t = 10.0625
+        (base, frac) = tfrac(t, milliseconds=10)
+        self.assertEqual(base, 10.06)
+        self.assertEqual(frac,  0.0025)
+
+    def test_tfrac_milliseconds_04(self):
+        t = 1.0078121
+        # Note that the last digit is lost due to rounding to microsecond scale.
+        (base, frac) = tfrac(t, milliseconds=1)
+        self.assertEqual(base, 1.007)
+        self.assertEqual(frac, 0.000812)
+
+    def test_tfrac_milliseconds_05(self):
+        t = 1.0078056
+        # Note that the last digit is lost due to rounding to microsecond scale.
+        (base, frac) = tfrac(t, milliseconds=1)
+        self.assertEqual(base, 1.007)
+        self.assertEqual(frac, 0.000806)
 
 
 #----------------------------------------------------------------
 # trange tests
 #----------------------------------------------------------------
 
-def test_trange_hours_01():
-    with set_timezone(UTC):
-        r = trange(0, 1, hours=1)
-        assert r == []
-
-def test_trange_hours_01_Alice_Springs():
-    # Australia/North (UTC+09:30, never DST)
-    with set_timezone(ALICE_SPRINGS):
-        r = trange(0, 1, hours=1)
-        assert r == []
-
-def test_trange_hours_01_Honolulu():
-    # Pacific/Honolulu (UTC-10:00, never DST)
-    with set_timezone(HONOLULU):
-        r = trange(0, 1, hours=1)
-        assert r == []
-
-def test_trange_hours_02():
-    with set_timezone(UTC):
-        r = trange(-1, 1, hours=1)
-        assert r == [0.0]
-
-def test_trange_hours_02_Alice_Springs():
-    # Australia/North (UTC+09:30, never DST)
-    with set_timezone(ALICE_SPRINGS):
-        r = trange(-1, 1, hours=1)
-        assert r == []
-
-def test_trange_hours_02_Honolulu():
-    # Pacific/Honolulu (UTC-10:00, never DST)
-    with set_timezone(HONOLULU):
-        r = trange(-1, 1, hours=1)
-        assert r == [0.0]
-
-def test_trange_hours_03():
-    with set_timezone(UTC):
-        r = trange(0, 3600, hours=1)
-        assert r == [0.0, 3600.0]
-
-def test_trange_hours_03_Alice_Springs():
-    # Australia/North (UTC+09:30, never DST)
-    with set_timezone(ALICE_SPRINGS):
-        r = trange(0, 3600, hours=1)
-        assert r == [1800.0]
-
-def test_trange_hours_03_Honolulu():
-    # Pacific/Honolulu (UTC-10:00, never DST)
-    with set_timezone(HONOLULU):
-        r = trange(0, 3600, hours=1)
-        assert r == [0.0, 3600.0]
-
-def test_trange_hours_04():
-    with set_timezone(UTC):
-        r = trange(-3600, 3600, hours=1)
-        assert r == [-3600.0, 0.0, 3600.0]
-
-def test_trange_hours_Alice_Springs():
-    # Australia/North (UTC+09:30, never DST)
-    with set_timezone(ALICE_SPRINGS):
-        r = trange(-3600, 3600, hours=1)
-        assert r == [-1800.0, 1800.0]
-
-def test_trange_hours_04_Honolulu():
-    # Pacific/Honolulu (UTC-10:00, never DST)
-    with set_timezone(HONOLULU):
-        r = trange(-3600, 3600, hours=1)
-        assert r == [-3600.0, 0.0, 3600.0]
-
-def test_trange_hours_05():
-    with set_timezone(UTC):
-        r = trange(-10, 3610, hours=1)
-        assert r == [0.0, 3600.0]
-
-def test_trange_hours_06():
-    with set_timezone(UTC):
-        r = trange(-10, 7210, hours=1)
-        assert r == [0.0, 3600.0, 7200.0]
-
-def test_trange_hours_07():
-    with set_timezone(UTC):
-        r = trange(-10, 7210, hours=2)
-        assert r == [0.0, 7200.0]
-
-def test_trange_hours_07_Alice_Springs():
-    # Australia/North (UTC+09:30, never DST)
-    with set_timezone(ALICE_SPRINGS):
-        r = trange(-10, 7210, hours=2)
-        assert r == [1800.0]
-
-def test_trange_hours_07_Honolulu():
-    # Pacific/Honolulu (UTC-10:00, never DST)
-    with set_timezone(HONOLULU):
-        r = trange(-10, 7210, hours=2)
-        assert r == [0.0, 7200.0]
-
-def test_trange_seconds_01():
-    r = trange(0, 1, seconds=1)
-    assert r == [0.0, 1.0]
-
-def test_trange_seconds_02():
-    r = trange(0, 10, seconds=1)
-    assert r == list(sm.xrange(11))
-
-def test_trange_seconds_03():
-    r = trange(0, 1.5, seconds=1)
-    assert r == [0.0, 1.0]
-
-def test_trange_milliseconds_01():
-    r = trange(0, 0.1, milliseconds=1)
-    assert np.allclose(np.array(r), np.linspace(0.0, 0.1, 101))
-
-def test_trange_milliseconds_02():
-    r = trange(-0.002, 0.001, milliseconds=1)
-    assert np.allclose(np.array(r), np.linspace(-0.002, 0.001, 4))
+class TestTRange(unittest.TestCase):
+
+    def test_trange_hours_01(self):
+        with set_timezone(UTC):
+            r = trange(0, 1, hours=1)
+            self.assertEqual(r, [])
+
+    def test_trange_hours_01_Alice_Springs(self):
+        # Australia/North (UTC+09:30, never DST)
+        with set_timezone(ALICE_SPRINGS):
+            r = trange(0, 1, hours=1)
+            self.assertEqual(r, [])
+
+    def test_trange_hours_01_Honolulu(self):
+        # Pacific/Honolulu (UTC-10:00, never DST)
+        with set_timezone(HONOLULU):
+            r = trange(0, 1, hours=1)
+            self.assertEqual(r, [])
+
+    def test_trange_hours_02(self):
+        with set_timezone(UTC):
+            r = trange(-1, 1, hours=1)
+            self.assertEqual(r, [0.0])
+
+    def test_trange_hours_02_Alice_Springs(self):
+        # Australia/North (UTC+09:30, never DST)
+        with set_timezone(ALICE_SPRINGS):
+            r = trange(-1, 1, hours=1)
+            self.assertEqual(r, [])
+
+    def test_trange_hours_02_Honolulu(self):
+        # Pacific/Honolulu (UTC-10:00, never DST)
+        with set_timezone(HONOLULU):
+            r = trange(-1, 1, hours=1)
+            self.assertEqual(r, [0.0])
+
+    def test_trange_hours_03(self):
+        with set_timezone(UTC):
+            r = trange(0, 3600, hours=1)
+            self.assertEqual(r, [0.0, 3600.0])
+
+    def test_trange_hours_03_Alice_Springs(self):
+        # Australia/North (UTC+09:30, never DST)
+        with set_timezone(ALICE_SPRINGS):
+            r = trange(0, 3600, hours=1)
+            self.assertEqual(r, [1800.0])
+
+    def test_trange_hours_03_Honolulu(self):
+        # Pacific/Honolulu (UTC-10:00, never DST)
+        with set_timezone(HONOLULU):
+            r = trange(0, 3600, hours=1)
+            self.assertEqual(r, [0.0, 3600.0])
+
+    def test_trange_hours_04(self):
+        with set_timezone(UTC):
+            r = trange(-3600, 3600, hours=1)
+            self.assertEqual(r, [-3600.0, 0.0, 3600.0])
+
+    def test_trange_hours_Alice_Springs(self):
+        # Australia/North (UTC+09:30, never DST)
+        with set_timezone(ALICE_SPRINGS):
+            r = trange(-3600, 3600, hours=1)
+            self.assertEqual(r, [-1800.0, 1800.0])
+
+    def test_trange_hours_04_Honolulu(self):
+        # Pacific/Honolulu (UTC-10:00, never DST)
+        with set_timezone(HONOLULU):
+            r = trange(-3600, 3600, hours=1)
+            self.assertEqual(r, [-3600.0, 0.0, 3600.0])
+
+    def test_trange_hours_05(self):
+        with set_timezone(UTC):
+            r = trange(-10, 3610, hours=1)
+            self.assertEqual(r, [0.0, 3600.0])
+
+    def test_trange_hours_06(self):
+        with set_timezone(UTC):
+            r = trange(-10, 7210, hours=1)
+            self.assertEqual(r, [0.0, 3600.0, 7200.0])
+
+    def test_trange_hours_07(self):
+        with set_timezone(UTC):
+            r = trange(-10, 7210, hours=2)
+            self.assertEqual(r, [0.0, 7200.0])
+
+    def test_trange_hours_07_Alice_Springs(self):
+        # Australia/North (UTC+09:30, never DST)
+        with set_timezone(ALICE_SPRINGS):
+            r = trange(-10, 7210, hours=2)
+            self.assertEqual(r, [1800.0])
+
+    def test_trange_hours_07_Honolulu(self):
+        # Pacific/Honolulu (UTC-10:00, never DST)
+        with set_timezone(HONOLULU):
+            r = trange(-10, 7210, hours=2)
+            self.assertEqual(r, [0.0, 7200.0])
+
+    def test_trange_seconds_01(self):
+        r = trange(0, 1, seconds=1)
+        self.assertEqual(r, [0.0, 1.0])
+
+    def test_trange_seconds_02(self):
+        r = trange(0, 10, seconds=1)
+        self.assertEqual(r, list(range(11)))
+
+    def test_trange_seconds_03(self):
+        r = trange(0, 1.5, seconds=1)
+        self.assertEqual(r, [0.0, 1.0])
+
+    def test_trange_milliseconds_01(self):
+        r = trange(0, 0.1, milliseconds=1)
+        nptest.assert_allclose(np.array(r), np.linspace(0.0, 0.1, 101))
+
+    def test_trange_milliseconds_02(self):
+        r = trange(-0.002, 0.001, milliseconds=1)
+        nptest.assert_allclose(
+            np.array(r), np.linspace(-0.002, 0.001, 4), rtol=1e-5, atol=1e-5)
 
 
 #----------------------------------------------------------------
@@ -425,42 +428,47 @@ def test_trange_milliseconds_02():
 
 # Could use more tests here... --WW
 
-def test_time_scale_seconds_01():
-    ts = TimeScale(seconds=1)
-    ticks = ts.ticks(0, 10)
-    assert (np.array(ticks) == np.linspace(0.0, 10.0, 11)).all()
+class TestTimeScale(unittest.TestCase):
 
-def test_time_scale_seconds_02():
-    ts = TimeScale(seconds=2)
-    ticks = ts.ticks(0, 10)
-    assert (np.array(ticks) == np.linspace(0.0, 10.0, 6)).all()
+    def test_time_scale_seconds_01(self):
+        ts = TimeScale(seconds=1)
+        ticks = ts.ticks(0, 10)
+        nptest.assert_array_equal(
+            np.array(ticks), np.linspace(0.0, 10.0, 11))
 
-def test_time_scale_milliseconds_01():
-    ts = TimeScale(milliseconds=1)
-    ticks = ts.ticks(0, 0.1)
-    assert len(ticks) == 11
-    assert (np.array(ticks) == np.linspace(0.0, 0.1, 11)).all()
+    def test_time_scale_seconds_02(self):
+        ts = TimeScale(seconds=2)
+        ticks = ts.ticks(0, 10)
+        nptest.assert_array_equal(np.array(ticks), np.linspace(0.0, 10.0, 6))
 
-def test_time_scale_with_formatter():
-    """ Regression test for TimeScale() with formatter keyword.
+    def test_time_scale_milliseconds_01(self):
+        ts = TimeScale(milliseconds=1)
+        ticks = ts.ticks(0, 0.1)
+        self.assertEqual(len(ticks), 11)
+        nptest.assert_array_equal(np.array(ticks), np.linspace(0.0, 0.1, 11))
 
-    Using the formatter keyword in the constructor of TimeScale
-    could raise a KeyError.  This test passes if no exception is
-    raised.
-    """
-    ts = TimeScale(seconds=1, formatter=TimeFormatter())
-    ts = TimeScale(minutes=1, formatter=TimeFormatter())
+    def test_time_scale_with_formatter(self):
+        """ Regression test for TimeScale() with formatter keyword.
+
+        Using the formatter keyword in the constructor of TimeScale
+        could raise a KeyError.  This test passes if no exception is
+        raised.
+        """
+        ts = TimeScale(seconds=1, formatter=TimeFormatter())
+        ts = TimeScale(minutes=1, formatter=TimeFormatter())
 
 
 #----------------------------------------------------------------
 # CalendarScaleSystem tests
 #----------------------------------------------------------------
 
-def test_calendar_scale_system_01():
-    css = CalendarScaleSystem()
-    ticks = css.ticks(0,10)
-    assert len(ticks) == 11
-    assert (np.array(ticks) == np.linspace(0,10,11)).all()
+class TestCalendarScaleSystem(unittest.TestCase):
+
+    def test_calendar_scale_system_01(self):
+        css = CalendarScaleSystem()
+        ticks = css.ticks(0,10)
+        self.assertEqual(len(ticks), 11)
+        nptest.assert_array_equal(np.array(ticks), np.linspace(0,10,11))
 
 
 # TODO: Add more tests of the ticks() and labels() methods of
diff --git a/chaco/scales/time_scale_test_case.py b/chaco/scales/tests/test_time_scale_resolution.py
similarity index 84%
rename from chaco/scales/time_scale_test_case.py
rename to chaco/scales/tests/test_time_scale_resolution.py
--- a/chaco/scales/time_scale_test_case.py
+++ b/chaco/scales/tests/test_time_scale_resolution.py
@@ -1,13 +1,11 @@
-from __future__ import print_function
-
 from itertools import starmap
 from datetime import datetime as DT
 
-from .scales import ScaleSystem
-from .time_scale import dt_to_sec, trange, TimeScale, HMSScales
-from .formatters import TimeFormatter
+from ..scales import ScaleSystem
+from ..time_scale import dt_to_sec, trange, TimeScale, HMSScales
+from ..formatters import TimeFormatter
 
-from .scales_test_case import TicksTestCase
+from .test_scales import TicksTestCase
 
 
 def DTS(*args, **kw):
@@ -50,13 +48,10 @@ def test_microseconds(self):
         # so an increment of, say, 3 microseconds is only about a factor of 10
         # more than machine precision.
         base = DTS(2005, 3, 15, 10, 45, 10)
-        print("base: ", base)
         start = base + 0.0000027
         end   = base + 0.0000177
         ticks = trange(start, end, microseconds=5)
         desired = [base+i for i in (5e-6, 10e-6, 15e-6)]
-        print("ticks:   ", ticks)
-        print("desired: ", desired)
         self.check_ticks(ticks, desired)
 
     def test_milliseconds(self):
@@ -74,8 +69,6 @@ def test_daily(self):
         secs_per_day = 24*3600
         ticks = trange(base, base + secs_per_day*5, days=1)
         desired = [base+i*secs_per_day for i in range(6)]
-        print("ticks:   ", ticks)
-        print("desired: ", desired)
         self.check_ticks(ticks, desired)
 
     def test_daily_leap(self):
@@ -96,13 +89,10 @@ def test_multiday_increment(self):
         start = DTS(2005, 1, 1)
         ticks = trange(start, start + 9*24*3600, days=3)
         desired = [start+i*3*24*3600 for i in range(4)]
-        print("ticks: ", ticks, " desired: ", desired)
         self.check_ticks(ticks, desired)
 
 
-
 class TimeScaleTestCase(TicksTestCase):
-    """ This exercises a single TimeScale set at various resolutions """
 
     def test_hourly(self):
         ts = TimeScale(hours=1)
@@ -152,15 +142,12 @@ def test_microsecond(self):
         end = base + 9.2e-6
         ticks = ts.ticks(start, end)
         desired = [base+i for i in (3e-6, 4e-6, 5e-6, 6e-6, 7e-6, 8e-6, 9e-6)]
-        print("ticks:   ", ticks)
-        print("desired: ", desired)
         self.check_ticks(ticks, desired)
 
 
 class CalendarScaleSystemTestCase(TicksTestCase):
-    """ This exercises the ability of multiple TimeScale objects to play well
-    within a single ScaleSystem.
-    """
+    # This exercises the ability of multiple TimeScale objects to play well
+    # within a single ScaleSystem.
 
     def test_hourly_scales(self):
         scales = [TimeScale(seconds=dt) for dt in (1, 5, 15, 30)] + \
@@ -180,30 +167,19 @@ class TimeFormatterTestCase(TicksTestCase):
     def test_widths(self):
         fmt = TimeFormatter()
         scale = TimeScale(minutes = 5)
-        test_intervals = ([(2005,3,15,10,30), (2005,3,15,10,50), 50],
-                          )
-        print()
+        test_intervals = ([(2005,3,15,10,30), (2005,3,15,10,50), 50],)
+        expected = (4.0, 12.0)
         for start, end, width in test_intervals:
             est_width = scale.label_width(DTS(*start), DTS(*end), char_width=width)
-            print(start, end, end=" ")
-            print(" avail:", width, "est:", est_width[1], "numlabels:", est_width[0])
-        return
+            self.assertEqual(est_width, expected)
 
     def test_labels(self):
         fmt = TimeFormatter()
         scale = ScaleSystem(*HMSScales)
-
-        test_intervals = ([(2005,3,15,10,30), (2005,3,15,10,50), 150],
-                          )
-        print()
+        expected_labels = ['{}m'.format(m) for m in range(30, 51)]
+        
+        test_intervals = ([(2005,3,15,10,30), (2005,3,15,10,50), 150],)
         for start, end, width in test_intervals:
             labels = scale.labels(DTS(*start), DTS(*end), char_width=width)
-            print(start, end, " avail:", width, end=" ")
-            print(" used:", sum([len(x[1]) for x in labels]), end=" ")
-            print(labels)
-        return
-
-
-if __name__ == "__main__":
-    import nose
-    nose.run()
+            labels = [label for (_, label) in labels]
+            self.assertEqual(labels, expected_labels)
diff --git a/chaco/shell/tests/__init__.py b/chaco/shell/tests/__init__.py
new file mode 100644
diff --git a/chaco/shell/tests/make_data_sources_test_case.py b/chaco/shell/tests/test_make_data_sources.py
similarity index 91%
rename from chaco/shell/tests/make_data_sources_test_case.py
rename to chaco/shell/tests/test_make_data_sources.py
--- a/chaco/shell/tests/make_data_sources_test_case.py
+++ b/chaco/shell/tests/test_make_data_sources.py
@@ -1,8 +1,5 @@
-
 import unittest
 
-import six
-
 import numpy as np
 from numpy.testing.utils import assert_almost_equal
 from chaco.shell.plot_maker import make_data_sources
@@ -16,7 +13,6 @@ def test_1D_single(self):
         sources = make_data_sources(session, "none", ary)
         assert_almost_equal(sources[0][0].get_data(), np.arange(len(ary)))
         assert_almost_equal(sources[0][1].get_data(), ary)
-        return
 
     def test_1d_multiple(self):
         session = None
@@ -31,10 +27,4 @@ def test_1d_multiple(self):
         assert_almost_equal(sources[0][1].get_data(), s)
         assert_almost_equal(sources[1][1].get_data(), c)
         assert_almost_equal(sources[2][1].get_data(), t)
-        return
-
-
-if __name__ == "__main__":
-    unittest.main()
 
-# EOF
diff --git a/chaco/tests/component_tests.py b/chaco/tests/component_tests.py
deleted file mode 100644
--- a/chaco/tests/component_tests.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from enable.api import Component
-
-
-def test_padding_init():
-    """ Make sure that padding traits passed to the constructor get set in the
-    correct order.
-    """
-    c = Component()
-    assert c.padding_top == 0
-    assert c.padding_bottom == 0
-    assert c.padding_left == 0
-    assert c.padding_right == 0
-    c = Component(padding=50)
-    assert c.padding_top == 50
-    assert c.padding_bottom == 50
-    assert c.padding_left == 50
-    assert c.padding_right == 50
-    c = Component(padding=50, padding_top=15)
-    assert c.padding_top == 15
-    assert c.padding_bottom == 50
-    assert c.padding_left == 50
-    assert c.padding_right == 50
-    c = Component(padding=50, padding_bottom=15)
-    assert c.padding_top == 50
-    assert c.padding_bottom == 15
-    assert c.padding_left == 50
-    assert c.padding_right == 50
-    c = Component(padding=50, padding_left=15)
-    assert c.padding_top == 50
-    assert c.padding_bottom == 50
-    assert c.padding_left == 15
-    assert c.padding_right == 50
-    c = Component(padding=50, padding_right=15)
-    assert c.padding_top == 50
-    assert c.padding_bottom == 50
-    assert c.padding_left == 50
-    assert c.padding_right == 15
-
-def test_padding_trait_default():
-    class PaddedComponent(Component):
-        padding_top = 50
-    c = PaddedComponent()
-    assert c.padding_top == 50
-    assert c.padding_bottom == 0
-    assert c.padding_left == 0
-    assert c.padding_right == 0
-    c = PaddedComponent(padding_left=15)
-    assert c.padding_top == 50
-    assert c.padding_bottom == 0
-    assert c.padding_left == 15
-    assert c.padding_right == 0
-
diff --git a/chaco/tests/array_plot_data_test_case.py b/chaco/tests/test_array_plot_data.py
similarity index 96%
rename from chaco/tests/array_plot_data_test_case.py
rename to chaco/tests/test_array_plot_data.py
--- a/chaco/tests/array_plot_data_test_case.py
+++ b/chaco/tests/test_array_plot_data.py
@@ -49,8 +49,3 @@ def test_data_changed_events(self):
         with self.monitor_events(plot_data) as events:
             plot_data.del_data('Grumpy')
             self.assertEqual(events, [{'removed': ['Grumpy']}])
-
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tests/arraydatasource_test_case.py b/chaco/tests/test_arraydatasource.py
similarity index 99%
rename from chaco/tests/arraydatasource_test_case.py
rename to chaco/tests/test_arraydatasource.py
--- a/chaco/tests/arraydatasource_test_case.py
+++ b/chaco/tests/test_arraydatasource.py
@@ -271,15 +271,8 @@ def test_basic_set_get(self):
         pd = PointDataSource(myarray)
         self.assertTrue(allclose(myarray, pd._data))
         self.assertTrue(pd.value_dimension == "point")
-        return
 
     def test_bounds(self):
         myarray = self.create_array()
         pd = PointDataSource(myarray)
         self.assertEqual(pd.get_bounds(), ((0, 0), (9, 90)))
-        return
-
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tests/base_utils_test_case.py b/chaco/tests/test_base_utils.py
similarity index 99%
rename from chaco/tests/base_utils_test_case.py
rename to chaco/tests/test_base_utils.py
--- a/chaco/tests/base_utils_test_case.py
+++ b/chaco/tests/test_base_utils.py
@@ -1,9 +1,6 @@
 """
 Unit tests for utility functions in chaco.base
 """
-
-from __future__ import print_function
-
 import unittest
 from math import sqrt
 from numpy import arange, array, linspace, nan, ones
@@ -537,9 +534,4 @@ def test_all_inside_mask(self):
         x = linspace(1, 2, 101)
         mask = (x <= 1.4) | (x >= 1.6)
         result = intersect_range(x, 0.0, 3.0, mask)
-        print(mask ^ result)
         assert_array_equal(result, mask)
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tests/border_test_case.py b/chaco/tests/test_border.py
similarity index 99%
rename from chaco/tests/border_test_case.py
rename to chaco/tests/test_border.py
--- a/chaco/tests/border_test_case.py
+++ b/chaco/tests/test_border.py
@@ -5,7 +5,6 @@
         DONE *. draw_border output should match a similar draw_rect output
 """
 
-import nose
 import unittest
 
 from numpy import array, alltrue, ravel
diff --git a/chaco/tests/colormapper_test_case.py b/chaco/tests/test_colormapper.py
similarity index 98%
rename from chaco/tests/colormapper_test_case.py
rename to chaco/tests/test_colormapper.py
--- a/chaco/tests/colormapper_test_case.py
+++ b/chaco/tests/test_colormapper.py
@@ -153,7 +153,3 @@ def test_no_alpha(self):
 ##         colormap._recalculate()
 
 ##         print '**************', colormap._color_bands, colormap._value_bands
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tests/data_frame_plot_data_test_case.py b/chaco/tests/test_data_frame_plot_data.py
similarity index 100%
rename from chaco/tests/data_frame_plot_data_test_case.py
rename to chaco/tests/test_data_frame_plot_data.py
diff --git a/chaco/tests/data_view_test_case.py b/chaco/tests/test_data_view.py
similarity index 95%
rename from chaco/tests/data_view_test_case.py
rename to chaco/tests/test_data_view.py
--- a/chaco/tests/data_view_test_case.py
+++ b/chaco/tests/test_data_view.py
@@ -37,7 +37,3 @@ def test_range2d_changed(self):
         self.assertTrue(old_range.sources==[])
         self.assertTrue(dv.range2d.x_range is dv.index_mapper.range)
         self.assertTrue(dv.range2d.y_range is dv.value_mapper.range)
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tests/datarange_1d_test_case.py b/chaco/tests/test_datarange_1d.py
similarity index 98%
rename from chaco/tests/datarange_1d_test_case.py
rename to chaco/tests/test_datarange_1d.py
--- a/chaco/tests/datarange_1d_test_case.py
+++ b/chaco/tests/test_datarange_1d.py
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
 import unittest
 
 from numpy import arange, array, zeros, inf
@@ -96,7 +94,6 @@ def test_set_bounds4(self):
         # Now reset foo's range_updated flag and set the bounds with set_bounds().
         foo.range_updated = False
         foo.range.set_bounds(100.0, 'track')
-        print(foo.range.low, foo.range.high)
         # Verify the values.
         self.assertEqual(foo.range.low, 100.0)
         self.assertEqual(foo.range.high, 101.0)
@@ -293,8 +290,3 @@ def test_inf_in_source(self):
         r.sources.append(ds1)
         self.assertEqual(r.low, -inf)
         self.assertEqual(r.high, inf)
-
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tests/datarange_2d_test_case.py b/chaco/tests/test_datarange_2d.py
similarity index 98%
rename from chaco/tests/datarange_2d_test_case.py
rename to chaco/tests/test_datarange_2d.py
--- a/chaco/tests/datarange_2d_test_case.py
+++ b/chaco/tests/test_datarange_2d.py
@@ -215,10 +215,3 @@ def assert_ary_(desired, actual):
         assert_equal(actual, 'auto')
     for d in range(len(desired)):
         assert_equal(desired[d], actual[d])
-    return
-
-
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tests/default_colormaps_test_case.py b/chaco/tests/test_default_colormaps.py
similarity index 97%
rename from chaco/tests/default_colormaps_test_case.py
rename to chaco/tests/test_default_colormaps.py
--- a/chaco/tests/default_colormaps_test_case.py
+++ b/chaco/tests/test_default_colormaps.py
@@ -9,9 +9,6 @@
 # Thanks for using Enthought open source!
 #
 #------------------------------------------------------------------------------
-
-from __future__ import print_function
-
 import unittest
 
 import numpy as np
@@ -56,7 +53,6 @@ def test_discrete_colormaps_smoke(self):
         x = np.array([2, 4, 0])
         datarange = DataRange1D(low_setting=0, high_setting=4)
         for cmap_func in default_colormaps.discrete_color_map_functions:
-            print(cmap_func)
             cmapper = cmap_func(datarange)
             rgba = cmapper.map_screen(x)
             self.assertEqual(rgba.shape, (3, 4))
diff --git a/chaco/tests/discrete_colormapper_test_case.py b/chaco/tests/test_discrete_colormapper.py
similarity index 98%
rename from chaco/tests/discrete_colormapper_test_case.py
rename to chaco/tests/test_discrete_colormapper.py
--- a/chaco/tests/discrete_colormapper_test_case.py
+++ b/chaco/tests/test_discrete_colormapper.py
@@ -132,6 +132,3 @@ def colormap_function(range, **traits):
         for i in range(4):
             assert_array_almost_equal(b[:, i], array([0.0, 0.5, 0.75]))
 
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tests/errorbarplot_test_case.py b/chaco/tests/test_errorbarplot.py
similarity index 100%
rename from chaco/tests/errorbarplot_test_case.py
rename to chaco/tests/test_errorbarplot.py
diff --git a/chaco/tests/function_data_source_test_case.py b/chaco/tests/test_function_data_source.py
similarity index 100%
rename from chaco/tests/function_data_source_test_case.py
rename to chaco/tests/test_function_data_source.py
diff --git a/chaco/tests/grid_data_source_test_case.py b/chaco/tests/test_grid_data_source.py
similarity index 97%
rename from chaco/tests/grid_data_source_test_case.py
rename to chaco/tests/test_grid_data_source.py
--- a/chaco/tests/grid_data_source_test_case.py
+++ b/chaco/tests/test_grid_data_source.py
@@ -72,8 +72,3 @@ def test_metadata_changed(self):
     def test_metadata_items_changed(self):
         with self.assertTraitChanges(self.data_source, 'metadata_changed', count=1):
             self.data_source.metadata['new_metadata'] = True
-
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tests/grid_mapper_test_case.py b/chaco/tests/test_grid_mapper.py
similarity index 96%
rename from chaco/tests/grid_mapper_test_case.py
rename to chaco/tests/test_grid_mapper.py
--- a/chaco/tests/grid_mapper_test_case.py
+++ b/chaco/tests/test_grid_mapper.py
@@ -48,8 +48,3 @@ def test_map_data_scalar(self):
         screen_ary = (60, 0)
         result = self.mapper.map_data(screen_ary)
         assert_equal(result, [[6.0, 1.0]])
-
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tests/hittest_test_case.py b/chaco/tests/test_hittest.py
similarity index 97%
rename from chaco/tests/hittest_test_case.py
rename to chaco/tests/test_hittest.py
--- a/chaco/tests/hittest_test_case.py
+++ b/chaco/tests/test_hittest.py
@@ -64,7 +64,3 @@ def _test_plot(self, plot, line_plot, point):
         self.assertEqual(x, result[0])
         self.assertEqual(y, result[1])
         self.assertTrue(d < threshold)
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tests/image_data_test_case.py b/chaco/tests/test_image_data.py
similarity index 100%
rename from chaco/tests/image_data_test_case.py
rename to chaco/tests/test_image_data.py
diff --git a/chaco/tests/test_image_plot.py b/chaco/tests/test_image_plot.py
--- a/chaco/tests/test_image_plot.py
+++ b/chaco/tests/test_image_plot.py
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
 import os
 
 import tempfile
@@ -91,16 +89,6 @@ def calculate_rms(image_result, expected_image):
     return rms
 
 
-def verify_result_image(input_image, expected_image, **plot_kwargs):
-    # These tests were written assuming uint8 inputs.
-    assert input_image.dtype == np.uint8
-    assert expected_image.dtype == np.uint8
-    image_result = rendered_image_result(input_image, **plot_kwargs)
-    rms = calculate_rms(image_result, expected_image)
-    print("RMS =", rms)
-    assert rms < MAX_RMS_ERROR
-
-
 def plot_comparison(input_image, expected_image, **plot_kwargs):
     import matplotlib.pyplot as plt
 
@@ -116,62 +104,53 @@ def plot_comparison(input_image, expected_image, **plot_kwargs):
     plt.show()
 
 
-@unittest.skipIf(six.PY3, "Bug in the image plotter in python 3. See GH enthought/enable #95.")
-def test_horizontal_top_left():
-    # Horizontal orientation with top left origin renders original image.
-    verify_result_image(RGB, IMAGE, origin='top left')
-
-
-@unittest.skipIf(six.PY3, "Bug in the image plotter in python 3. See GH enthought/enable #95.")
-def test_horizontal_bottom_left():
-    # Horizontal orientation with bottom left origin renders a vertically
-    # flipped image.
-    verify_result_image(RGB, IMAGE[::-1], origin='bottom left')
-
-
-@unittest.skipIf(six.PY3, "Bug in the image plotter in python 3. See GH enthought/enable #95.")
-def test_horizontal_top_right():
-    # Horizontal orientation with top right origin renders a horizontally
-    # flipped image.
-    verify_result_image(RGB, IMAGE[:, ::-1], origin='top right')
-
-
-@unittest.skipIf(six.PY3, "Bug in the image plotter in python 3. See GH enthought/enable #95.")
-def test_horizontal_bottom_right():
-    # Horizontal orientation with top right origin renders an image flipped
-    # horizontally and vertically.
-    verify_result_image(RGB, IMAGE[::-1, ::-1], origin='bottom right')
-
-
-@unittest.skipIf(six.PY3, "Bug in the image plotter in python 3. See GH enthought/enable #95.")
-def test_vertical_top_left():
-    # Vertical orientation with top left origin renders transposed image.
-    verify_result_image(RGB, IMAGE.T, origin='top left', orientation='v')
-
-
-@unittest.skipIf(six.PY3, "Bug in the image plotter in python 3. See GH enthought/enable #95.")
-def test_vertical_bottom_left():
-    # Vertical orientation with bottom left origin renders transposed image
-    # that is vertically flipped.
-    verify_result_image(RGB, (IMAGE.T)[::-1],
-                        origin='bottom left', orientation='v')
-
-
-@unittest.skipIf(six.PY3, "Bug in the image plotter in python 3. See GH enthought/enable #95.")
-def test_vertical_top_right():
-    # Vertical orientation with top right origin renders transposed image
-    # that is horizontally flipped.
-    verify_result_image(RGB, (IMAGE.T)[:, ::-1],
-                        origin='top right', orientation='v')
-
-
-@unittest.skipIf(six.PY3, "Bug in the image plotter in python 3. See GH enthought/enable #95.")
-def test_vertical_bottom_right():
-    # Vertical orientation with bottom right origin renders transposed image
-    # that is flipped vertically and horizontally.
-    verify_result_image(RGB, (IMAGE.T)[::-1, ::-1],
-                        origin='bottom right', orientation='v')
-
-
-if __name__ == "__main__":
-    np.testing.run_module_suite()
+class TestResultImage(unittest.TestCase):
+
+    def verify_result_image(self, input_image, expected_image, **plot_kwargs):
+        # These tests were written assuming uint8 inputs.
+        self.assertEqual(input_image.dtype, np.uint8)
+        self.assertEqual(expected_image.dtype, np.uint8)
+        image_result = rendered_image_result(input_image, **plot_kwargs)
+        rms = calculate_rms(image_result, expected_image)
+        self.assertLess(rms, MAX_RMS_ERROR)
+
+    def test_horizontal_top_left(self):
+        # Horizontal orientation with top left origin renders original image.
+        self.verify_result_image(RGB, IMAGE, origin='top left')
+
+    def test_horizontal_bottom_left(self):
+        # Horizontal orientation with bottom left origin renders a vertically
+        # flipped image.
+        self.verify_result_image(RGB, IMAGE[::-1], origin='bottom left')
+
+    def test_horizontal_top_right(self):
+        # Horizontal orientation with top right origin renders a horizontally
+        # flipped image.
+        self.verify_result_image(RGB, IMAGE[:, ::-1], origin='top right')
+
+    def test_horizontal_bottom_right(self):
+        # Horizontal orientation with top right origin renders an image flipped
+        # horizontally and vertically.
+        self.verify_result_image(RGB, IMAGE[::-1, ::-1], origin='bottom right')
+
+    def test_vertical_top_left(self):
+        # Vertical orientation with top left origin renders transposed image.
+        self.verify_result_image(RGB, IMAGE.T, origin='top left', orientation='v')
+
+    def test_vertical_bottom_left(self):
+        # Vertical orientation with bottom left origin renders transposed image
+        # that is vertically flipped.
+        self.verify_result_image(RGB, (IMAGE.T)[::-1],
+                                 origin='bottom left', orientation='v')
+
+    def test_vertical_top_right(self):
+        # Vertical orientation with top right origin renders transposed image
+        # that is horizontally flipped.
+        self.verify_result_image(RGB, (IMAGE.T)[:, ::-1],
+                                 origin='top right', orientation='v')
+
+    def test_vertical_bottom_right(self):
+        # Vertical orientation with bottom right origin renders transposed image
+        # that is flipped vertically and horizontally.
+        self.verify_result_image(RGB, (IMAGE.T)[::-1, ::-1],
+                                 origin='bottom right', orientation='v')
diff --git a/chaco/tests/test_image_utils.py b/chaco/tests/test_image_utils.py
--- a/chaco/tests/test_image_utils.py
+++ b/chaco/tests/test_image_utils.py
@@ -1,3 +1,5 @@
+import unittest
+
 from numpy.testing import assert_allclose, assert_equal
 
 from chaco.image_utils import trim_screen_rect, X_PARAMS, Y_PARAMS
@@ -18,44 +20,43 @@ def assert_midpoints_equal(bounds_list):
         assert_equal(x_mid[0], x_mid[1])
 
 
-def test_viewer_zoomed_into_single_pixel():
-    screen_rect = [0, 0, 100, 100]
-    view_rect = [10, 11, 1, 2]
-    new_rect = trim_screen_rect(screen_rect, view_rect, SINGLE_PIXEL)
-    assert_allclose(new_rect, view_rect)
-
-
-def test_viewer_at_corner_of_single_image():
-    offset = 0.2
-    screen_rect = [1, 1, 1, 1]
-    new_size = [1-offset, 1-offset]
-
-    down_right = [1+offset, 1+offset, 1, 1]
-    new_rect = trim_screen_rect(screen_rect, down_right, SINGLE_PIXEL)
-    expected_rect = down_right[:2] + new_size
-    assert_midpoints_equal((new_rect, expected_rect))
-
-    up_left = [1-offset, 1-offset, 1, 1]
-    new_rect = trim_screen_rect(screen_rect, up_left, SINGLE_PIXEL)
-    expected_rect = [1, 1] + new_size
-    assert_midpoints_equal((new_rect, expected_rect))
-
-
-def test_viewer_zoomed_into_four_pixel_intersection():
-    screen_rect = [0, 0, 100, 100]  # 4-pixel intersection at (50, 50)
-    view_rectangles = ([49, 49, 2, 2],  # Centered pixel intersection
-                       [49, 49, 3, 3],  # Intersection at 1/3 of view
-                       [49, 49, 2, 3])  # Intersection at 1/2, 1/3 of view
-    for view_rect in view_rectangles:
-        new_rect = trim_screen_rect(screen_rect, view_rect, FOUR_PIXELS)
-        yield assert_midpoints_equal, (new_rect, screen_rect)
-
-
-def test_viewer_at_corner_of_four_pixel_image():
-    offset = 0.2
-    screen_rect = [1, 1, 1, 1]
-    view_rectangles = ([1+offset, 1+offset, 1, 1],  # Shifted down and right
-                       [1-offset, 1-offset, 1, 1])  # Shifted up and left
-    for view_rect in view_rectangles:
-        new_rect = trim_screen_rect(screen_rect, view_rect, FOUR_PIXELS)
-        yield assert_equal, new_rect, screen_rect
+class TestImageUtils(unittest.TestCase):
+
+    def test_viewer_zoomed_into_single_pixel(self):
+        screen_rect = [0, 0, 100, 100]
+        view_rect = [10, 11, 1, 2]
+        new_rect = trim_screen_rect(screen_rect, view_rect, SINGLE_PIXEL)
+        assert_allclose(new_rect, view_rect)
+
+    def test_viewer_at_corner_of_single_image(self):
+        offset = 0.2
+        screen_rect = [1, 1, 1, 1]
+        new_size = [1-offset, 1-offset]
+
+        down_right = [1+offset, 1+offset, 1, 1]
+        new_rect = trim_screen_rect(screen_rect, down_right, SINGLE_PIXEL)
+        expected_rect = down_right[:2] + new_size
+        assert_midpoints_equal((new_rect, expected_rect))
+
+        up_left = [1-offset, 1-offset, 1, 1]
+        new_rect = trim_screen_rect(screen_rect, up_left, SINGLE_PIXEL)
+        expected_rect = [1, 1] + new_size
+        assert_midpoints_equal((new_rect, expected_rect))
+
+    def test_viewer_zoomed_into_four_pixel_intersection(self):
+        screen_rect = [0, 0, 100, 100]  # 4-pixel intersection at (50, 50)
+        view_rectangles = ([49, 49, 2, 2],  # Centered pixel intersection
+                           [49, 49, 3, 3],  # Intersection at 1/3 of view
+                           [49, 49, 2, 3])  # Intersection at 1/2, 1/3 of view
+        for view_rect in view_rectangles:
+            new_rect = trim_screen_rect(screen_rect, view_rect, FOUR_PIXELS)
+            assert_midpoints_equal((new_rect, screen_rect))
+
+    def test_viewer_at_corner_of_four_pixel_image(self):
+        offset = 0.2
+        screen_rect = [1, 1, 1, 1]
+        view_rectangles = ([1+offset, 1+offset, 1, 1],  # Shifted down and right
+                           [1-offset, 1-offset, 1, 1])  # Shifted up and left
+        for view_rect in view_rectangles:
+            new_rect = trim_screen_rect(screen_rect, view_rect, FOUR_PIXELS)
+            assert_equal(new_rect, screen_rect)
diff --git a/chaco/tests/instantiation_order_test_case.py b/chaco/tests/test_instantiation_order.py
similarity index 93%
rename from chaco/tests/instantiation_order_test_case.py
rename to chaco/tests/test_instantiation_order.py
--- a/chaco/tests/instantiation_order_test_case.py
+++ b/chaco/tests/test_instantiation_order.py
@@ -27,7 +27,6 @@ def test_piecewise_construction(self):
         mapper.high_pos = 7.0
         screen_pts = mapper.map_screen(array([1,3,7]))
         self.assertTrue(tuple(screen_pts) == (1.0, 3.0, 7.0))
-        return
 
     def test_reverse_construction(self):
         mapper = LinearMapper()
@@ -45,9 +44,3 @@ def test_reverse_construction(self):
         self.assertTrue(r.high == 7)
         screen_pts = mapper.map_screen(array([1,3,7]))
         self.assertTrue(tuple(screen_pts) == (1.0, 3.0, 7.0))
-        return
-
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tests/jitterplot_test_case.py b/chaco/tests/test_jitterplot.py
similarity index 100%
rename from chaco/tests/jitterplot_test_case.py
rename to chaco/tests/test_jitterplot.py
diff --git a/chaco/tests/line_scatterplot_test_case.py b/chaco/tests/test_line_scatterplot.py
similarity index 100%
rename from chaco/tests/line_scatterplot_test_case.py
rename to chaco/tests/test_line_scatterplot.py
diff --git a/chaco/tests/linearmapper_test_case.py b/chaco/tests/test_linearmapper.py
similarity index 99%
rename from chaco/tests/linearmapper_test_case.py
rename to chaco/tests/test_linearmapper.py
--- a/chaco/tests/linearmapper_test_case.py
+++ b/chaco/tests/test_linearmapper.py
@@ -289,9 +289,3 @@ def test_update_high_pos_dont_stretch_data_with_zero(self):
         mapper.high_pos = 100.0
         result = mapper.map_screen(ary)
         assert_array_almost_equal(result, array([50, 60, 70, 80, 90, 100]))
-
-
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tests/logmapper_test_case.py b/chaco/tests/test_logmapper.py
similarity index 93%
rename from chaco/tests/logmapper_test_case.py
rename to chaco/tests/test_logmapper.py
--- a/chaco/tests/logmapper_test_case.py
+++ b/chaco/tests/test_logmapper.py
@@ -14,7 +14,6 @@ def test_basic(self):
         mapper = LogMapper(range=r, low_pos=50, high_pos=90)
         result = mapper.map_screen(ary)
         assert_equal(result, array([50, 60, 70, 80, 90]))
-        return
 
     def test_reversed(self):
         ary = array([1.0, 10.0, 100.0, 1000.0, 10000.0])
@@ -23,7 +22,6 @@ def test_reversed(self):
         mapper = LogMapper(range=r, low_pos=100, high_pos=0)
         result = mapper.map_screen(ary)
         assert_array_almost_equal(result, array([100, 75, 50, 25, 0]))
-        return
 
     def test_fractional(self):
         ary = array([0.0001, 0.001, 0.01])
@@ -32,7 +30,6 @@ def test_fractional(self):
         mapper = LogMapper(range=r, low_pos=0, high_pos=20)
         result = mapper.map_screen(ary)
         assert_array_almost_equal(result, [0, 10, 20])
-        return
 
     def test_zero(self):
         ary = array([0.0, 1.0, 10.0, 100.0, 1000.0])
@@ -41,7 +38,6 @@ def test_zero(self):
         mapper = LogMapper(range=r, low_pos=0, high_pos=30)
         result = mapper.map_screen(ary)
         assert_array_almost_equal(result, [0, 0, 10, 20, 30])
-        return
 
     def test_negative(self):
         ary = array([1.0, -1.0, -2.0, 10.0, 100.0, 1000.0])
@@ -50,7 +46,6 @@ def test_negative(self):
         mapper = LogMapper(range=r, low_pos=0, high_pos=30)
         result = mapper.map_screen(ary)
         assert_array_almost_equal(result, [0, 0, 0, 10, 20, 30])
-        return
 
     def test_fill_value(self):
         ary = array([1.0, -1.0, -2.0, 10.0, 100.0, 1000.0])
@@ -61,7 +56,6 @@ def test_fill_value(self):
         mapper.fill_value = 100.0
         result = mapper.map_screen(ary)
         assert_array_almost_equal(result, [0, 20, 20, 10, 20, 30])
-        return
 
     def test_nan(self):
         ary = array([1.0, nan, 10.0, nan, 100.0, 1000.0])
@@ -71,9 +65,3 @@ def test_nan(self):
         mapper.fill_value = 100.0
         result = mapper.map_screen(ary)
         assert_array_almost_equal(result, [0, 20, 10, 20, 20, 30])
-        return
-
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tests/multi_array_data_source_test_case.py b/chaco/tests/test_multi_array_data_source.py
similarity index 100%
rename from chaco/tests/multi_array_data_source_test_case.py
rename to chaco/tests/test_multi_array_data_source.py
diff --git a/chaco/tests/plot_test_case.py b/chaco/tests/test_plot.py
similarity index 100%
rename from chaco/tests/plot_test_case.py
rename to chaco/tests/test_plot.py
diff --git a/chaco/tests/plotcontainer_test_case.py b/chaco/tests/test_plotcontainer.py
similarity index 99%
rename from chaco/tests/plotcontainer_test_case.py
rename to chaco/tests/test_plotcontainer.py
--- a/chaco/tests/plotcontainer_test_case.py
+++ b/chaco/tests/test_plotcontainer.py
@@ -1,8 +1,6 @@
 import sys
 import unittest
 
-import six.moves as sm
-
 from chaco.api import HPlotContainer, OverlayPlotContainer, \
                                 PlotComponent, VPlotContainer, GridContainer
 from traits.api import Any, Tuple
@@ -13,7 +11,7 @@
 class ContainerTestCase(unittest.TestCase):
     def assert_tuple(self, t1, t2):
         self.assertEqual(len(t1), len(t2))
-        for i in sm.xrange(len(t1)):
+        for i in range(len(t1)):
             self.assertEqual(t1[i], t2[i])
 
 
@@ -644,7 +642,3 @@ def test_non_resizable(self):
         self.assert_tuple(ll.bounds, (100,100))
         self.assert_tuple(lr.position, (160,20))
         self.assert_tuple(lr.bounds, (100,100))
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tests/scatterplot_1d_test_case.py b/chaco/tests/test_scatterplot_1d.py
similarity index 100%
rename from chaco/tests/scatterplot_1d_test_case.py
rename to chaco/tests/test_scatterplot_1d.py
diff --git a/chaco/tests/scatterplot_renderers_test_case.py b/chaco/tests/test_scatterplot_renderers.py
similarity index 100%
rename from chaco/tests/scatterplot_renderers_test_case.py
rename to chaco/tests/test_scatterplot_renderers.py
diff --git a/chaco/tests/serializable_test_case.py b/chaco/tests/test_serializable.py
similarity index 87%
rename from chaco/tests/serializable_test_case.py
rename to chaco/tests/test_serializable.py
--- a/chaco/tests/serializable_test_case.py
+++ b/chaco/tests/test_serializable.py
@@ -1,4 +1,4 @@
-from __future__ import print_function
+import warnings
 
 import six.moves as sm
 import unittest
@@ -19,10 +19,11 @@ def compare_traits(self, a, b, trait_names=None):
             o1 = getattr(a,name)
             o2 = getattr(b,name)
             if isinstance(o1, list) or isinstance(o1, tuple):
-                print("Warning: Cowardly refusing to do deep compares")
+                raise RuntimeError(
+                    "Warning: Cowardly refusing to do deep compares"
+                )
             else:
                 self.assertTrue(o1 == o2)
-        return
 
     def test_basic_save(self):
         c = Circle(radius=5.0, name="c1", x=1.0, y=2.0)
@@ -30,7 +31,6 @@ def test_basic_save(self):
         for attrib in ("tools", "filled", "color", "x", "radius"):
             self.assertTrue(getattr(c, attrib) == getattr(c2, attrib))
         self.assertEqual(c2.y, 2.0)
-        return
 
     def test_basic_save2(self):
         p = Poly(numside=3, name="poly", x=3.0, y=4.0)
@@ -38,13 +38,7 @@ def test_basic_save2(self):
         for attrib in ("tools", "filled", "color", "x", "numsides", "length"):
             self.assertTrue(getattr(p, attrib) == getattr(p2, attrib))
         self.assertEqual(p2.y, 4.0)
-        return
 
 
 class PlotSerializationTestCase(unittest.TestCase):
     pass
-
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tests/speedups_test_case.py b/chaco/tests/test_speedups.py
similarity index 100%
rename from chaco/tests/speedups_test_case.py
rename to chaco/tests/test_speedups.py
diff --git a/chaco/tests/text_plot_1d_test_case.py b/chaco/tests/test_text_plot_1d.py
similarity index 100%
rename from chaco/tests/text_plot_1d_test_case.py
rename to chaco/tests/test_text_plot_1d.py
diff --git a/chaco/tests_with_backend/README.txt b/chaco/tests_with_backend/README.txt
--- a/chaco/tests_with_backend/README.txt
+++ b/chaco/tests_with_backend/README.txt
@@ -2,6 +2,3 @@
 More unit tests for Chaco
 =========================
 These tests require an installed backend for traitsui such as PyQt or wx. 
-They are separated so that TravisCI test-running can be done on different 
-folders for the different versions of python because it is tricky to 
-install PyQt on the Python 2.6 virtualenv.
\ No newline at end of file
diff --git a/chaco/tests_with_backend/__init__.py b/chaco/tests_with_backend/__init__.py
new file mode 100644
diff --git a/chaco/tests_with_backend/create_2d_test_case.py b/chaco/tests_with_backend/create_2d_test_case.py
deleted file mode 100644
--- a/chaco/tests_with_backend/create_2d_test_case.py
+++ /dev/null
@@ -1,150 +0,0 @@
-from __future__ import with_statement
-
-from chaco.api import Plot, ArrayPlotData
-
-from traits.api import HasTraits, Instance
-from enable.component_editor import ComponentEditor
-from traitsui.api import Item, View
-
-import numpy as np
-
-import nose
-from chaco.tests._tools import store_exceptions_on_all_threads, assert_raises
-
-
-class PlotViewer(HasTraits):
-    plot = Instance(Plot)
-    traits_view = View(Item('plot', editor=ComponentEditor()))
-
-
-def test_bounds_2d_case():
-    # test for bug: contour and image plots should support the case where
-    # xbounds and ybounds are 2d arrays resulting from meshgrids
-
-    xs = np.linspace(-10,10,200)
-    ys = np.linspace(-10,10,400)
-    x, y = np.meshgrid(xs,ys)
-    z = x + y
-
-    plotdata = ArrayPlotData()
-    plotdata.set_data("z", z)
-
-    plot = Plot(plotdata)
-    plot.contour_plot("z", xbounds=x, ybounds=y)
-
-    # try to display it, that's when the exception is raised
-    with store_exceptions_on_all_threads():
-        pv = PlotViewer(plot=plot)
-        pv.edit_traits()
-
-
-def test_process_2d_bounds_cell_plot():
-    # behavior: _process_2d_bounds accepts all possible ways to set x and y
-    # bounds in 2d plots and returns a 1d array with equally spaced
-    # intervals between the lower and upper bound of the data. The number
-    # of elements in the 1d array must be of one element larger than the
-    # shape of the data, because this is cell data.
-
-    height, width = 20, 10
-    array_data = np.ones(shape=(height, width))
-    plot = Plot()
-
-    # bounds is None : infer from array_data shape
-    xs = plot._process_2d_bounds(None, array_data, 1, cell_plot=True)
-    assert xs.shape[0] == width + 1
-    ys = plot._process_2d_bounds(None, array_data, 0, cell_plot=True)
-    assert ys.shape[0] == height + 1
-
-    # bounds is a tuple : it defines lower and upper range
-    bounds = (1.0, 100.0)
-    xs = plot._process_2d_bounds(bounds, array_data, 1, cell_plot=True)
-    assert xs.shape[0] == width + 1
-    assert xs[0] == bounds[0] and xs[-1] == bounds[1]
-
-    # bounds is a 1D array: the first and last elements are used to create
-    # equally spaced intervals. Bounds must be of one element larger than the
-    # corresponding axis in array_data, or it will raise a Value error
-    bounds = np.zeros((height+1, ))
-    bounds[0], bounds[-1] = 0.2, 21.3
-    ys = plot._process_2d_bounds(bounds, array_data, 0, cell_plot=True)
-    assert ys.shape[0] == height + 1
-    assert ys[0] == bounds[0] and ys[-1] == bounds[-1]
-    with assert_raises(ValueError):
-        bounds = np.zeros((width // 2, ))
-        plot._process_2d_bounds(bounds, array_data, 0, cell_plot=True)
-
-    # bounds is a 2D array: the first and last elements along the appropriate
-    # axis are used to create equally spaced intervals.
-    # The size of the bounds must be the same as the data array, or this
-    # sill raise a ValueError
-    xbounds, ybounds = np.meshgrid(np.arange(width+1), np.arange(height+1))
-
-    xs = plot._process_2d_bounds(xbounds, array_data, 1, cell_plot=True)
-    assert xs.shape[0] == width + 1
-    assert xs[0] == xbounds[0,0] and xs[-1] == xbounds[0,-1]
-    with assert_raises(ValueError):
-        plot._process_2d_bounds(xbounds[:, :5], array_data, 1, cell_plot=True)
-
-    ys = plot._process_2d_bounds(ybounds, array_data, 0, cell_plot=True)
-    assert ys.shape[0] == height + 1
-    assert ys[0] == ybounds[0,0] and ys[-1] == ybounds[-1,0]
-    with assert_raises(ValueError):
-        plot._process_2d_bounds(ybounds[:5, :], array_data, 0, cell_plot=True)
-
-
-def test_process_2d_bounds_vertex_data():
-    # behavior: _process_2d_bounds accepts all possible ways to set x and y
-    # bounds in 2d plots and returns a 1d array with equally spaced
-    # intervals between the lower and upper bound of the data. The number
-    # of elements in the 1d array must be the same as the shape of the data,
-    # because this is vertex data.
-
-    height, width = 20, 10
-    array_data = np.ones(shape=(height, width))
-    plot = Plot()
-
-    # bounds is None : infer from array_data shape
-    xs = plot._process_2d_bounds(None, array_data, 1, cell_plot=False)
-    assert xs.shape[0] == width
-    ys = plot._process_2d_bounds(None, array_data, 0, cell_plot=False)
-    assert ys.shape[0] == height
-
-    # bounds is a tuple : it defines lower and upper range
-    bounds = (1.0, 100.0)
-    xs = plot._process_2d_bounds(bounds, array_data, 1, cell_plot=False)
-    assert xs.shape[0] == width
-    assert xs[0] == bounds[0] and xs[-1] == bounds[1]
-
-    # bounds is a 1D array: the first and last elements are used to create
-    # equally spaced intervals. Bounds must be of one element larger than the
-    # corresponding axis in array_data, or it will raise a Value error
-    bounds = np.zeros((height, ))
-    bounds[0], bounds[-1] = 0.2, 21.3
-    ys = plot._process_2d_bounds(bounds, array_data, 0, cell_plot=False)
-    assert ys.shape[0] == height
-    assert ys[0] == bounds[0] and ys[-1] == bounds[-1]
-    with assert_raises(ValueError):
-        bounds = np.zeros((width // 2, ))
-        plot._process_2d_bounds(bounds, array_data, 0, cell_plot=False)
-
-    # bounds is a 2D array: the first and last elements along the appropriate
-    # axis are used to create equally spaced intervals.
-    # The size of the bounds must be the same as the data array, or this
-    # sill raise a ValueError
-    xbounds, ybounds = np.meshgrid(np.arange(width), np.arange(height))
-
-    xs = plot._process_2d_bounds(xbounds, array_data, 1, cell_plot=False)
-    assert xs.shape[0] == width
-    assert xs[0] == xbounds[0,0] and xs[-1] == xbounds[0,-1]
-    with assert_raises(ValueError):
-        plot._process_2d_bounds(xbounds[:, :5], array_data, 1, cell_plot=False)
-
-    ys = plot._process_2d_bounds(ybounds, array_data, 0, cell_plot=False)
-    assert ys.shape[0] == height
-    assert ys[0] == ybounds[0,0] and ys[-1] == ybounds[-1,0]
-    with assert_raises(ValueError):
-        plot._process_2d_bounds(ybounds[:5, :], array_data, 0, cell_plot=False)
-
-
-if __name__ == '__main__':
-    nose.main()
diff --git a/chaco/tests_with_backend/highlight_tool_test_case.py b/chaco/tests_with_backend/highlight_tool_test_case.py
deleted file mode 100644
--- a/chaco/tests_with_backend/highlight_tool_test_case.py
+++ /dev/null
@@ -1,53 +0,0 @@
-from traits.api import HasTraits, Instance
-from traitsui.api import Item, View
-from enable.component_editor import ComponentEditor
-
-from chaco.api import Plot, ArrayPlotData
-from chaco.tools.highlight_tool import HighlightTool
-
-import numpy as np
-
-import nose
-
-
-class PlotViewer(HasTraits):
-    plot = Instance(Plot)
-    traits_view = View(Item('plot', editor=ComponentEditor()))
-
-
-class MockEvent(HasTraits):
-    pass
-
-
-def test_highlight_on_log_plot():
-    # test for bug: the highlight tool raises an exception when used on
-    # a loglog plot
-
-    x = np.linspace(1, 15, 200)
-
-    plotdata = ArrayPlotData()
-    plotdata.set_data("x", x)
-    plotdata.set_data("y", x*x)
-
-    plot = Plot(plotdata)
-    plot.plot(("x", "y"), index_scale='log', value_scale='log')
-
-    # necessary for the machinery involved in _find_curve
-    plot.datasources["x"].sort_order = 'ascending'
-    plot.datasources["y"].sort_order = 'ascending'
-
-    # add the highlight tool
-    htool = HighlightTool(plot, threshold=20.)
-    plot.tools.append(htool)
-
-    # we create a view of the plot so that the screen bounds are set
-    pv = PlotViewer(plot=plot)
-    pv.edit_traits()
-
-    # this should not raise an exception
-    event = MockEvent(x=170., y=60.)
-    htool._find_curve(plot.plots['plot0'], event)
-
-
-if __name__ == '__main__':
-    nose.main()
diff --git a/chaco/tests_with_backend/test_2d_case.py b/chaco/tests_with_backend/test_2d_case.py
new file mode 100644
--- /dev/null
+++ b/chaco/tests_with_backend/test_2d_case.py
@@ -0,0 +1,156 @@
+import unittest
+
+from chaco.api import Plot, ArrayPlotData
+
+from traits.api import HasTraits, Instance
+from traits.etsconfig.api import ETSConfig
+from enable.component_editor import ComponentEditor
+from traitsui.api import Item, View
+
+import numpy as np
+
+from chaco.tests._tools import store_exceptions_on_all_threads, assert_raises
+
+
+class PlotViewer(HasTraits):
+    plot = Instance(Plot)
+    traits_view = View(Item('plot', editor=ComponentEditor()))
+
+
+@unittest.skipIf(
+    ETSConfig.toolkit=='null', "Skip on 'null' toolkit")
+class Test2DCase(unittest.TestCase):
+
+    def test_bounds_2d_case(self):
+        # test for bug: contour and image plots should support the case where
+        # xbounds and ybounds are 2d arrays resulting from meshgrids
+
+        xs = np.linspace(-10,10,200)
+        ys = np.linspace(-10,10,400)
+        x, y = np.meshgrid(xs,ys)
+        z = x + y
+
+        plotdata = ArrayPlotData()
+        plotdata.set_data("z", z)
+
+        plot = Plot(plotdata)
+        plot.contour_plot("z", xbounds=x, ybounds=y)
+
+        # try to display it, that's when the exception is raised
+        with store_exceptions_on_all_threads():
+            pv = PlotViewer(plot=plot)
+            pv.edit_traits()
+
+    def test_process_2d_bounds_cell_plot(self):
+        # behavior: _process_2d_bounds accepts all possible ways to set x and y
+        # bounds in 2d plots and returns a 1d array with equally spaced
+        # intervals between the lower and upper bound of the data. The number
+        # of elements in the 1d array must be of one element larger than the
+        # shape of the data, because this is cell data.
+
+        height, width = 20, 10
+        array_data = np.ones(shape=(height, width))
+        plot = Plot()
+
+        # bounds is None : infer from array_data shape
+        xs = plot._process_2d_bounds(None, array_data, 1, cell_plot=True)
+        self.assertEqual(xs.shape[0], width + 1)
+        ys = plot._process_2d_bounds(None, array_data, 0, cell_plot=True)
+        self.assertEqual(ys.shape[0], height + 1)
+
+        # bounds is a tuple : it defines lower and upper range
+        bounds = (1.0, 100.0)
+        xs = plot._process_2d_bounds(bounds, array_data, 1, cell_plot=True)
+        self.assertEqual(xs.shape[0], width + 1)
+        self.assertEqual(xs[0], bounds[0])
+        self.assertEqual(xs[-1], bounds[1])
+
+        # bounds is a 1D array: the first and last elements are used to create
+        # equally spaced intervals. Bounds must be of one element larger than the
+        # corresponding axis in array_data, or it will raise a Value error
+        bounds = np.zeros((height+1, ))
+        bounds[0], bounds[-1] = 0.2, 21.3
+        ys = plot._process_2d_bounds(bounds, array_data, 0, cell_plot=True)
+        self.assertEqual(ys.shape[0], height + 1)
+        self.assertEqual(ys[0], bounds[0])
+        self.assertEqual(ys[-1], bounds[-1])
+        with assert_raises(ValueError):
+            bounds = np.zeros((width // 2, ))
+            plot._process_2d_bounds(bounds, array_data, 0, cell_plot=True)
+
+        # bounds is a 2D array: the first and last elements along the appropriate
+        # axis are used to create equally spaced intervals.
+        # The size of the bounds must be the same as the data array, or this
+        # sill raise a ValueError
+        xbounds, ybounds = np.meshgrid(np.arange(width+1), np.arange(height+1))
+
+        xs = plot._process_2d_bounds(xbounds, array_data, 1, cell_plot=True)
+        self.assertEqual(xs.shape[0], width + 1)
+        self.assertEqual(xs[0], xbounds[0,0])
+        self.assertEqual(xs[-1], xbounds[0,-1])
+        with assert_raises(ValueError):
+            plot._process_2d_bounds(xbounds[:, :5], array_data, 1, cell_plot=True)
+
+        ys = plot._process_2d_bounds(ybounds, array_data, 0, cell_plot=True)
+        self.assertEqual(ys.shape[0], height + 1)
+        self.assertEqual(ys[0], ybounds[0,0])
+        self.assertEqual(ys[-1], ybounds[-1,0])
+        with assert_raises(ValueError):
+            plot._process_2d_bounds(ybounds[:5, :], array_data, 0, cell_plot=True)
+
+    def test_process_2d_bounds_vertex_data(self):
+        # behavior: _process_2d_bounds accepts all possible ways to set x and y
+        # bounds in 2d plots and returns a 1d array with equally spaced
+        # intervals between the lower and upper bound of the data. The number
+        # of elements in the 1d array must be the same as the shape of the data,
+        # because this is vertex data.
+
+        height, width = 20, 10
+        array_data = np.ones(shape=(height, width))
+        plot = Plot()
+
+        # bounds is None : infer from array_data shape
+        xs = plot._process_2d_bounds(None, array_data, 1, cell_plot=False)
+        self.assertEqual(xs.shape[0], width)
+        ys = plot._process_2d_bounds(None, array_data, 0, cell_plot=False)
+        self.assertEqual(ys.shape[0], height)
+
+        # bounds is a tuple : it defines lower and upper range
+        bounds = (1.0, 100.0)
+        xs = plot._process_2d_bounds(bounds, array_data, 1, cell_plot=False)
+        self.assertEqual(xs.shape[0], width)
+        self.assertEqual(xs[0], bounds[0])
+        self.assertEqual(xs[-1], bounds[1])
+
+        # bounds is a 1D array: the first and last elements are used to create
+        # equally spaced intervals. Bounds must be of one element larger than the
+        # corresponding axis in array_data, or it will raise a Value error
+        bounds = np.zeros((height, ))
+        bounds[0], bounds[-1] = 0.2, 21.3
+        ys = plot._process_2d_bounds(bounds, array_data, 0, cell_plot=False)
+        self.assertEqual(ys.shape[0], height)
+        self.assertEqual(ys[0], bounds[0])
+        self.assertEqual(ys[-1], bounds[-1])
+        with assert_raises(ValueError):
+            bounds = np.zeros((width // 2, ))
+            plot._process_2d_bounds(bounds, array_data, 0, cell_plot=False)
+
+        # bounds is a 2D array: the first and last elements along the appropriate
+        # axis are used to create equally spaced intervals.
+        # The size of the bounds must be the same as the data array, or this
+        # sill raise a ValueError
+        xbounds, ybounds = np.meshgrid(np.arange(width), np.arange(height))
+
+        xs = plot._process_2d_bounds(xbounds, array_data, 1, cell_plot=False)
+        self.assertEqual(xs.shape[0], width)
+        self.assertEqual(xs[0], xbounds[0,0])
+        self.assertEqual(xs[-1], xbounds[0,-1])
+        with assert_raises(ValueError):
+            plot._process_2d_bounds(xbounds[:, :5], array_data, 1, cell_plot=False)
+
+        ys = plot._process_2d_bounds(ybounds, array_data, 0, cell_plot=False)
+        self.assertEqual(ys.shape[0], height)
+        self.assertEqual(ys[0], ybounds[0,0])
+        self.assertEqual(ys[-1], ybounds[-1,0])
+        with assert_raises(ValueError):
+            plot._process_2d_bounds(ybounds[:5, :], array_data, 0, cell_plot=False)
diff --git a/chaco/tests_with_backend/test_highlight_tool.py b/chaco/tests_with_backend/test_highlight_tool.py
new file mode 100644
--- /dev/null
+++ b/chaco/tests_with_backend/test_highlight_tool.py
@@ -0,0 +1,54 @@
+import unittest
+
+from traits.api import HasTraits, Instance
+from traits.etsconfig.api import ETSConfig
+from traitsui.api import Item, View
+from enable.component_editor import ComponentEditor
+
+from chaco.api import Plot, ArrayPlotData
+from chaco.tools.highlight_tool import HighlightTool
+
+import numpy as np
+
+
+class PlotViewer(HasTraits):
+    plot = Instance(Plot)
+    traits_view = View(Item('plot', editor=ComponentEditor()))
+
+
+class MockEvent(HasTraits):
+    pass
+
+
+@unittest.skipIf(
+    ETSConfig.toolkit=='null', "Skip on 'null' toolkit")
+class TestHighlightTool(unittest.TestCase):
+
+    def test_highlight_on_log_plot(self):
+        # test for bug: the highlight tool raises an exception when used on
+        # a loglog plot
+
+        x = np.linspace(1, 15, 200)
+
+        plotdata = ArrayPlotData()
+        plotdata.set_data("x", x)
+        plotdata.set_data("y", x*x)
+
+        plot = Plot(plotdata)
+        plot.plot(("x", "y"), index_scale='log', value_scale='log')
+
+        # necessary for the machinery involved in _find_curve
+        plot.datasources["x"].sort_order = 'ascending'
+        plot.datasources["y"].sort_order = 'ascending'
+
+        # add the highlight tool
+        htool = HighlightTool(plot, threshold=20.)
+        plot.tools.append(htool)
+
+        # we create a view of the plot so that the screen bounds are set
+        pv = PlotViewer(plot=plot)
+        pv.edit_traits()
+
+        # this should not raise an exception
+        event = MockEvent(x=170., y=60.)
+        htool._find_curve(plot.plots['plot0'], event)
diff --git a/chaco/tools/tests/better_zoom_test_case.py b/chaco/tools/tests/test_better_zoom_tool.py
similarity index 100%
rename from chaco/tools/tests/better_zoom_test_case.py
rename to chaco/tools/tests/test_better_zoom_tool.py
diff --git a/chaco/tools/tests/pan_tool_test_case.py b/chaco/tools/tests/test_pan_tool.py
similarity index 95%
rename from chaco/tools/tests/pan_tool_test_case.py
rename to chaco/tools/tests/test_pan_tool.py
--- a/chaco/tools/tests/pan_tool_test_case.py
+++ b/chaco/tools/tests/test_pan_tool.py
@@ -33,9 +33,3 @@ def test_restrict_to_data_with_empty_source(self):
         self.mouse_up(interactor=tool, x=1.0, y=1.0)
         self.assertEqual((x_range.low, x_range.high), x_bounds)
         self.assertEqual((y_range.low, y_range.high), y_bounds)
-
-
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tools/tests/range_selection_test_case.py b/chaco/tools/tests/test_range_selection.py
similarity index 93%
rename from chaco/tools/tests/range_selection_test_case.py
rename to chaco/tools/tests/test_range_selection.py
--- a/chaco/tools/tests/range_selection_test_case.py
+++ b/chaco/tools/tests/test_range_selection.py
@@ -61,15 +61,15 @@ def test_selection_no_warning(self):
         renderer = plot.plot(('x', 'y'))[0]
         tool = RangeSelection(renderer)
         with warnings.catch_warnings(record=True) as w:
+            # Ignore warnings coming from Traits
+            warnings.filterwarnings(
+                "ignore", 'elementwise == comparison failed'
+            )
             tool.selection = np.array([2.0, 3.0])
+
         self.assertEqual(w, [])
 
         # Accept tuples and lists and None
         tool.selection = (1.5, 3.5)
         tool.selection = [1.0, 2.0]
         tool.selection = None
-
-
-if __name__ == '__main__':
-    import nose
-    nose.run()
diff --git a/chaco/tools/tests/range_zoom_test_case.py b/chaco/tools/tests/test_range_zoom.py
similarity index 100%
rename from chaco/tools/tests/range_zoom_test_case.py
rename to chaco/tools/tests/test_range_zoom.py
diff --git a/chaco/tools/tests/scatter_inspector_test_case.py b/chaco/tools/tests/test_scatter_inspector.py
similarity index 100%
rename from chaco/tools/tests/scatter_inspector_test_case.py
rename to chaco/tools/tests/test_scatter_inspector.py