diff --git a/Makefile b/Makefile
index badebe2a05fdac9b88700c92c481b7b202e2644c..9eb36408864ae93d1eb78ec47bc6d8f493122342 100644
--- a/Makefile
+++ b/Makefile
@@ -30,6 +30,11 @@ motivation.html : motivation.md _layouts/slides.html
 	$(INCLUDES) \
 	-o $@ $<
 
+## unittest : Run unit test (for Python 2 and 3)
+unittest: tools/check tools/validation_helpers.py tools/test_check.py
+	cd tools/ && python2 test_check.py
+	cd tools/ && python3 test_check.py
+
 ## commands : Display available commands.
 commands : Makefile
 	@sed -n 's/^##//p' $<
diff --git a/tools/check b/tools/check
index abd096ffddb509f5b5e6a853ea73410e311f84ae..ecfaaba9724a5971dc5f08007fb0bca1b6f3a644 100755
--- a/tools/check
+++ b/tools/check
@@ -406,7 +406,8 @@ class TopicPageValidator(MarkdownValidator):
 class MotivationPageValidator(MarkdownValidator):
     """Validate motivation.md"""
     DOC_HEADERS = {"layout": vh.is_str,
-                   "title": vh.is_str}
+                   "title": vh.is_str,
+                   "subtitle": vh.is_str}
     # TODO: How to validate? May be a mix of reveal.js (HTML) + markdown.
 
 
diff --git a/tools/test_check.py b/tools/test_check.py
index 2f0966f68010dabbaa0c37b102acba04ce580cf4..003e26e82ef8086335a4c1e14565ad1c0a58acd7 100644
--- a/tools/test_check.py
+++ b/tools/test_check.py
@@ -1,6 +1,18 @@
 #! /usr/bin/env python
 
-import imp, logging, os, unittest
+"""
+Unit and functional tests for markdown lesson template validator.
+
+Some of these tests require looking for example files, which exist only on
+the gh-pages branch.   Some tests may therefore fail on branch "core".
+"""
+
+
+import imp
+import logging
+import os
+import unittest
+
 check = imp.load_source("check",  # Import non-.py file
                         os.path.join(os.path.dirname(__file__), "check"))
 
@@ -8,7 +20,7 @@ check = imp.load_source("check",  # Import non-.py file
 check.start_logging(level=logging.DEBUG)
 
 MARKDOWN_DIR = os.path.abspath(
-                os.path.join(os.path.dirname(__file__), os.pardir))
+    os.path.join(os.path.dirname(__file__), os.pardir))
 
 
 class BaseTemplateTest(unittest.TestCase):
@@ -16,9 +28,6 @@ class BaseTemplateTest(unittest.TestCase):
     SAMPLE_FILE = "" # Path to a file that should pass all tests
     VALIDATOR = check.MarkdownValidator
 
-    def setUp(self):
-        self.sample_validator = self.VALIDATOR(self.SAMPLE_FILE)
-
     def _create_validator(self, markdown):
         """Create validator object from markdown string; useful for failures"""
         return self.VALIDATOR(markdown=markdown)
@@ -45,7 +54,8 @@ class TestIndexPage(BaseTemplateTest):
     VALIDATOR = check.IndexPageValidator
 
     def test_sample_file_passes_validation(self):
-        res = self.sample_validator.validate()
+        sample_validator = self.VALIDATOR(self.SAMPLE_FILE)
+        res = sample_validator.validate()
         self.assertTrue(res)
 
     def test_headers_missing_hrs(self):
@@ -89,11 +99,33 @@ keywords: this is not a list
     # TESTS INVOLVING SECTION TITLES/HEADINGS
     def test_index_has_valid_section_headings(self):
         """The provided index page"""
-        res = self.sample_validator._validate_section_heading_order()
+        validator = self._create_validator("""## Topics
+
+1.  [Topic Title One](01-one.html)
+2.  [Topic Title Two](02-two.html)
+
+## Other Resources
+
+*   [Motivation](motivation.html)
+*   [Reference Guide](reference.html)
+*   [Next Steps](discussion.html)
+*   [Instructor's Guide](instructors.html)""")
+        res = validator._validate_section_heading_order()
         self.assertTrue(res)
 
     def test_index_fail_when_section_heading_absent(self):
-        res = self.sample_validator.ast.has_section_heading("Fake heading")
+        validator = self._create_validator("""## Topics
+
+1.  [Topic Title One](01-one.html)
+2.  [Topic Title Two](02-two.html)
+
+## Other Resources
+
+*   [Motivation](motivation.html)
+*   [Reference Guide](reference.html)
+*   [Next Steps](discussion.html)
+*   [Instructor's Guide](instructors.html)""")
+        res = validator.ast.has_section_heading("Fake heading")
         self.assertFalse(res)
 
     def test_fail_when_section_heading_is_wrong_level(self):
@@ -122,7 +154,6 @@ Paragraph of introductory material.
 *   [Instructor's Guide](instructors.html)""")
         self.assertFalse(validator._validate_section_heading_order())
 
-
     def test_fail_when_section_headings_in_wrong_order(self):
         validator = self._create_validator("""---
 layout: lesson
@@ -182,11 +213,15 @@ Paragraph of introductory material.
 
     # TESTS INVOLVING LINKS TO OTHER CONTENT
     def test_file_links_validate(self):
-        res = self.sample_validator._validate_links()
+        """Verify that all links in a sample file validate.
+        Involves checking for example files; may fail on "core" branch"""
+        sample_validator = self.VALIDATOR(self.SAMPLE_FILE)
+        res = sample_validator._validate_links()
         self.assertTrue(res)
 
     def test_html_link_to_extant_md_file_passes(self):
-        """Verify that an HTML link with corresponding MD file will pass"""
+        """Verify that an HTML link with corresponding MD file will pass
+        Involves checking for example files; may fail on "core" branch"""
         validator = self._create_validator("""[Topic Title One](01-one.html)""")
         self.assertTrue(validator._validate_links())
 
@@ -195,6 +230,8 @@ Paragraph of introductory material.
 
         For now this just tests that the regex handles #anchors.
          It doesn't validate that the named anchor exists in the md file
+
+        Involves checking for example files; may fail on "core" branch
         """
         validator = self._create_validator("""[Topic Title One](01-one.html#anchor)""")
         self.assertTrue(validator._validate_links())
@@ -206,7 +243,6 @@ Paragraph of introductory material.
 SQLite uses the integers 0 and 1 for the former, and represents the latter as discussed [earlier](#a:dates).""")
         self.assertTrue(validator._validate_links())
 
-
     def test_missing_markdown_file_fails_validation(self):
         """Fail validation when an html file is linked without corresponding
             markdown file"""
@@ -227,7 +263,8 @@ SQLite uses the integers 0 and 1 for the former, and represents the latter as di
         self.assertFalse(validator._validate_links())
 
     def test_finds_image_asset(self):
-        """Image asset is found"""
+        """Image asset is found in the expected file location
+        Involves checking for example files; may fail on "core" branch"""
         validator = self._create_validator(
             """![this is the image's title](fig/example.svg "this is the image's alt text")""")
         self.assertTrue(validator._validate_links())
@@ -239,7 +276,9 @@ SQLite uses the integers 0 and 1 for the former, and represents the latter as di
         self.assertFalse(validator._validate_links())
 
     def test_non_html_link_finds_csv(self):
-        """Look for CSV file in appropriate folder"""
+        """Look for CSV file in appropriate folder
+        Involves checking for example files; may fail on "core" branch
+        """
         validator = self._create_validator(
             """Use [this CSV](data/data.csv) for the exercise.""")
         self.assertTrue(validator._validate_links())
@@ -257,7 +296,8 @@ class TestTopicPage(BaseTemplateTest):
     VALIDATOR = check.TopicPageValidator
 
     def test_sample_file_passes_validation(self):
-        res = self.sample_validator.validate()
+        sample_validator = self.VALIDATOR(self.SAMPLE_FILE)
+        res = sample_validator.validate()
         self.assertTrue(res)
 
 
@@ -267,7 +307,8 @@ class TestMotivationPage(BaseTemplateTest):
     VALIDATOR = check.MotivationPageValidator
 
     def test_sample_file_passes_validation(self):
-        res = self.sample_validator.validate()
+        sample_validator = self.VALIDATOR(self.SAMPLE_FILE)
+        res = sample_validator.validate()
         self.assertTrue(res)
 
 
@@ -326,7 +367,8 @@ Key Word 2
         self.assertTrue(validator._validate_glossary())
 
     def test_sample_file_passes_validation(self):
-        res = self.sample_validator.validate()
+        sample_validator = self.VALIDATOR(self.SAMPLE_FILE)
+        res = sample_validator.validate()
         self.assertTrue(res)
 
 
@@ -336,7 +378,8 @@ class TestInstructorPage(BaseTemplateTest):
     VALIDATOR = check.InstructorPageValidator
 
     def test_sample_file_passes_validation(self):
-        res = self.sample_validator.validate()
+        sample_validator = self.VALIDATOR(self.SAMPLE_FILE)
+        res = sample_validator.validate()
         self.assertTrue(res)
 
 
@@ -345,7 +388,8 @@ class TestLicensePage(BaseTemplateTest):
     VALIDATOR = check.LicensePageValidator
 
     def test_sample_file_passes_validation(self):
-        res = self.sample_validator.validate()
+        sample_validator = self.VALIDATOR(self.SAMPLE_FILE)
+        res = sample_validator.validate()
         self.assertTrue(res)
 
     def test_modified_file_fails_validation(self):
@@ -361,7 +405,8 @@ class TestDiscussionPage(BaseTemplateTest):
     VALIDATOR = check.DiscussionPageValidator
 
     def test_sample_file_passes_validation(self):
-        res = self.sample_validator.validate()
+        sample_validator = self.VALIDATOR(self.SAMPLE_FILE)
+        res = sample_validator.validate()
         self.assertTrue(res)