Merge branch 'master' of github.com:wolever/parameterized
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 8559710..17cc2e3 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,5 +1,7 @@
-0.6.2 (???)
+0.6.2 (2018-03-11)
     * Make sure that `setUp` and `tearDown` methods work correctly (#40)
+    * Raise a ValueError when input is empty (thanks @danielbradburn;
+      https://github.com/wolever/parameterized/pull/48)
 
 0.6.1 (2017-03-21)
     * Rename package from nose-parameterized to parameterized. A
diff --git a/parameterized/parameterized.py b/parameterized/parameterized.py
index 96de811..775a1cd 100644
--- a/parameterized/parameterized.py
+++ b/parameterized/parameterized.py
@@ -13,6 +13,12 @@
 
 from unittest import TestCase
 
+try:
+    from unittest import SkipTest
+except ImportError:
+    class SkipTest(Exception):
+        pass
+
 PY3 = sys.version_info[0] == 3
 PY2 = sys.version_info[0] == 2
 
@@ -40,6 +46,8 @@
 
 _param = namedtuple("param", "args kwargs")
 
+def skip_on_empty_helper(*a, **kw):
+    raise SkipTest("parameterized input is empty")
 
 def reapply_patches_if_need(func):
 
@@ -57,7 +65,6 @@
             func = patch_obj.decorate_callable(func)
     return func
 
-
 def delete_patches_if_need(func):
     if hasattr(func, 'patchings'):
         func.patchings[:] = []
@@ -305,9 +312,10 @@
                 assert_equal(a + b, expected)
         """
 
-    def __init__(self, input, doc_func=None):
+    def __init__(self, input, doc_func=None, skip_on_empty=False):
         self.get_input = self.input_as_callable(input)
         self.doc_func = doc_func or default_doc_func
+        self.skip_on_empty = skip_on_empty
 
     def __call__(self, test_func):
         self.assert_not_in_testcase_subclass()
@@ -343,9 +351,21 @@
                     if test_self is not None:
                         delattr(test_cls, test_func.__name__)
                     wrapper.__doc__ = original_doc
-        wrapper.parameterized_input = self.get_input()
+
+        input = self.get_input()
+        if not input:
+            if not self.skip_on_empty:
+                raise ValueError(
+                    "Parameters iterable is empty (hint: use "
+                    "`parameterized([], skip_on_empty=True)` to skip "
+                    "this test when the input is empty)"
+                )
+            wrapper = wraps(test_func)(lambda: skip_on_empty_helper())
+
+        wrapper.parameterized_input = input
         wrapper.parameterized_func = test_func
         test_func.__name__ = "_parameterized_original_%s" %(test_func.__name__, )
+
         return wrapper
 
     def param_as_nose_tuple(self, test_self, func, num, p):
@@ -410,7 +430,8 @@
         return [ param.from_decorator(p) for p in input_values ]
 
     @classmethod
-    def expand(cls, input, name_func=None, doc_func=None, **legacy):
+    def expand(cls, input, name_func=None, doc_func=None, skip_on_empty=False,
+               **legacy):
         """ A "brute force" method of parameterizing test cases. Creates new
             test cases and injects them into the namespace that the wrapped
             function is being defined in. Useful for parameterizing tests in
@@ -447,6 +468,16 @@
             frame_locals = frame[0].f_locals
 
             paramters = cls.input_as_callable(input)()
+
+            if not paramters:
+                if not skip_on_empty:
+                    raise ValueError(
+                        "Parameters iterable is empty (hint: use "
+                        "`parameterized.expand([], skip_on_empty=True)` to skip "
+                        "this test when the input is empty)"
+                    )
+                return wraps(f)(lambda: skip_on_empty_helper())
+
             for num, p in enumerate(paramters):
                 name = name_func(f, num, p)
                 # If the original function has patches applied by 'mock.patch',
diff --git a/parameterized/test.py b/parameterized/test.py
index 2cfded3..f470127 100644
--- a/parameterized/test.py
+++ b/parameterized/test.py
@@ -3,12 +3,11 @@
 import inspect
 import mock
 from unittest import TestCase
-from nose.tools import assert_equal
-from nose.plugins.skip import SkipTest
+from nose.tools import assert_equal, assert_raises
 
 from .parameterized import (
     PY3, PY2, parameterized, param, parameterized_argument_value_pairs,
-    short_repr, detect_runner,
+    short_repr, detect_runner, SkipTest
 )
 
 def assert_contains(haystack, needle):
@@ -283,6 +282,32 @@
     else:
         raise AssertionError("Expected exception not raised")
 
+
+def test_helpful_error_on_empty_iterable_input():
+    try:
+        parameterized([])(lambda: None)
+    except ValueError as e:
+        assert_contains(str(e), "iterable is empty")
+    else:
+        raise AssertionError("Expected exception not raised")
+
+def test_skip_test_on_empty_iterable():
+    func = parameterized([], skip_on_empty=True)(lambda: None)
+    assert_raises(SkipTest, func)
+
+
+def test_helpful_error_on_empty_iterable_input_expand():
+    try:
+        class ExpectErrorOnEmptyInput(TestCase):
+            @parameterized.expand([])
+            def test_expect_error(self):
+                pass
+    except ValueError as e:
+        assert_contains(str(e), "iterable is empty")
+    else:
+        raise AssertionError("Expected exception not raised")
+
+
 expect("generator", [
     "test_wrapped_iterable_input('foo')",
 ])