Index: sasmodels/kernelpy.py
===================================================================
--- sasmodels/kernelpy.py	(revision 4bfd2776ad7d85e00cad95f2a94c6553b27a3e7c)
+++ sasmodels/kernelpy.py	(revision e62a134977ad95083b632930400ef7aa9f96129d)
@@ -12,4 +12,11 @@
 from . import details
 from .generate import F64
+
+try:
+    from typing import Union
+except:
+    pass
+else:
+    DType = Union[None, str, np.dtype]
 
 class PyModel(object):
Index: sasmodels/model_test.py
===================================================================
--- sasmodels/model_test.py	(revision 4bfd2776ad7d85e00cad95f2a94c6553b27a3e7c)
+++ sasmodels/model_test.py	(revision e62a134977ad95083b632930400ef7aa9f96129d)
@@ -58,5 +58,14 @@
 from .modelinfo import expand_pars
 
+try:
+    from typing import List, Iterator, Callable
+except ImportError:
+    pass
+else:
+    from .modelinfo import ParameterTable, ParameterSet, TestCondition, ModelInfo
+    from .kernelpy import PyModel, PyInput, PyKernel, DType
+
 def call_ER(model_info, pars):
+    # type: (ModelInfo, ParameterSet) -> float
     """
     Call the model ER function using *values*. *model_info* is either
@@ -72,4 +81,5 @@
 
 def call_VR(model_info, pars):
+    # type: (ModelInfo, ParameterSet) -> float
     """
     Call the model VR function using *pars*.
@@ -85,4 +95,5 @@
 
 def _vol_pars(model_info, pars):
+    # type: (ModelInfo, ParameterSet) -> Tuple[np.ndarray, np.ndarray]
     vol_pars = [get_weights(p, pars)
                 for p in model_info.parameters.call_parameters
@@ -93,4 +104,5 @@
 
 def make_suite(loaders, models):
+    # type: (List[str], List[str]) -> unittest.TestSuite
     """
     Construct the pyunit test suite.
@@ -102,6 +114,5 @@
     *models* is the list of models to test, or *["all"]* to test all models.
     """
-
-    ModelTestCase = _hide_model_case_from_nosetests()
+    ModelTestCase = _hide_model_case_from_nose()
     suite = unittest.TestSuite()
 
@@ -160,5 +171,6 @@
 
 
-def _hide_model_case_from_nosetests():
+def _hide_model_case_from_nose():
+    # type: () -> type
     class ModelTestCase(unittest.TestCase):
         """
@@ -171,4 +183,5 @@
         def __init__(self, test_name, model_info, test_method_name,
                      platform, dtype):
+            # type: (str, ModelInfo, str, str, DType) -> None
             self.test_name = test_name
             self.info = model_info
@@ -180,9 +193,10 @@
 
         def run_all(self):
+            # type: () -> None
             smoke_tests = [
-                [{}, 0.1, None],
-                [{}, (0.1, 0.1), None],
-                [{}, 'ER', None],
-                [{}, 'VR', None],
+                ({}, 0.1, None),
+                ({}, (0.1, 0.1), None),
+                ({}, 'ER', None),
+                ({}, 'VR', None),
                 ]
 
@@ -207,6 +221,7 @@
 
         def run_one(self, model, test):
-            pars, x, y = test
-            pars = expand_pars(self.info.parameters, pars)
+            # type: (PyModel, TestCondition) -> None
+            user_pars, x, y = test
+            pars = expand_pars(self.info.parameters, user_pars)
 
             if not isinstance(y, list):
@@ -222,6 +237,6 @@
                 actual = [call_VR(model.info, pars)]
             elif isinstance(x[0], tuple):
-                Qx, Qy = zip(*x)
-                q_vectors = [np.array(Qx), np.array(Qy)]
+                qx, qy = zip(*x)
+                q_vectors = [np.array(qx), np.array(qy)]
                 kernel = model.make_kernel(q_vectors)
                 actual = call_kernel(kernel, pars)
@@ -247,4 +262,5 @@
 
 def is_near(target, actual, digits=5):
+    # type: (float, float, int) -> bool
     """
     Returns true if *actual* is within *digits* significant digits of *target*.
@@ -255,4 +271,5 @@
 
 def main():
+    # type: () -> int
     """
     Run tests given is sys.argv.
@@ -288,8 +305,8 @@
   python -m sasmodels.model_test [-v] [opencl|dll] model1 model2 ...
 
-If -v is included on the command line, then use verboe output.
+If -v is included on the command line, then use verbose output.
 
 If neither opencl nor dll is specified, then models will be tested with
-both opencl and dll; the compute target is ignored for pure python models.
+both OpenCL and dll; the compute target is ignored for pure python models.
 
 If model1 is 'all', then all except the remaining models will be tested.
@@ -306,4 +323,5 @@
 
 def model_tests():
+    # type: () -> Iterator[Callable[[], None]
     """
     Test runner visible to nosetests.
Index: sasmodels/modelinfo.py
===================================================================
--- sasmodels/modelinfo.py	(revision 0d99a6a86eab6856acde298e639fa14daee2968b)
+++ sasmodels/modelinfo.py	(revision e62a134977ad95083b632930400ef7aa9f96129d)
@@ -640,4 +640,5 @@
     info.variant_info = getattr(kernel_module, 'variant_info', None)
     info.source = getattr(kernel_module, 'source', [])
+    # TODO: check the structure of the tests
     info.tests = getattr(kernel_module, 'tests', [])
     info.ER = getattr(kernel_module, 'ER', None)
@@ -712,6 +713,6 @@
     source = None           # type: List[str]
     tests = None            # type: List[TestCondition]
-    ER = None               # type: Optional[Callable[[np.ndarray, ...], float]]
-    VR = None               # type: Optional[Callable[[np.ndarray, ...], float]]
+    ER = None               # type: Optional[Callable[[np.ndarray, ...], np.ndarray]]
+    VR = None               # type: Optional[Callable[[np.ndarray, ...], Tuple[np.ndarray, np.ndarray]]]
     form_volume = None      # type: Optional[Callable[[np.ndarray, ...], float]]
     Iq = None               # type: Optional[Callable[[np.ndarray, ...], np.ndarray]]
