177 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			177 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Python
		
	
	
	
| """
 | |
| generic mechanism for marking python functions. 
 | |
| 
 | |
| By using the ``py.test.mark`` helper you can instantiate
 | |
| decorators that will set named meta data on test functions. 
 | |
| 
 | |
| Marking a single function 
 | |
| ----------------------------------------------------
 | |
| 
 | |
| You can "mark" a test function with meta data like this::
 | |
| 
 | |
|     @py.test.mark.webtest
 | |
|     def test_send_http():
 | |
|         ... 
 | |
| 
 | |
| This will set a "Marker" instance as a function attribute named "webtest". 
 | |
| You can also specify parametrized meta data like this::
 | |
| 
 | |
|     @py.test.mark.webtest(firefox=30)
 | |
|     def test_receive():
 | |
|         ...
 | |
| 
 | |
| The named marker can be accessed like this later::
 | |
| 
 | |
|     test_receive.webtest.kwargs['firefox'] == 30
 | |
| 
 | |
| In addition to set key-value pairs you can also use positional arguments::
 | |
| 
 | |
|     @py.test.mark.webtest("triangular")
 | |
|     def test_receive():
 | |
|         ...
 | |
| 
 | |
| and later access it with ``test_receive.webtest.args[0] == 'triangular``.
 | |
| 
 | |
| .. _`scoped-marking`:
 | |
| 
 | |
| Marking whole classes or modules 
 | |
| ----------------------------------------------------
 | |
| 
 | |
| If you are programming with Python2.6 you may use ``py.test.mark`` decorators
 | |
| with classes to apply markers to all its test methods::
 | |
| 
 | |
|     @py.test.mark.webtest
 | |
|     class TestClass:
 | |
|         def test_startup(self):
 | |
|             ...
 | |
|         def test_startup_and_more(self):
 | |
|             ...
 | |
| 
 | |
| This is equivalent to directly applying the decorator to the
 | |
| two test functions. 
 | |
| 
 | |
| To remain compatible with Python2.5 you can also set a 
 | |
| ``pytestmark`` attribute on a TestClass like this::
 | |
| 
 | |
|     import py
 | |
| 
 | |
|     class TestClass:
 | |
|         pytestmark = py.test.mark.webtest
 | |
| 
 | |
| or if you need to use multiple markers you can use a list::
 | |
| 
 | |
|     import py
 | |
| 
 | |
|     class TestClass:
 | |
|         pytestmark = [py.test.mark.webtest, pytest.mark.slowtest]
 | |
| 
 | |
| You can also set a module level marker::
 | |
| 
 | |
|     import py
 | |
|     pytestmark = py.test.mark.webtest
 | |
| 
 | |
| in which case it will be applied to all functions and 
 | |
| methods defined in the module.  
 | |
| 
 | |
| Using "-k MARKNAME" to select tests
 | |
| ----------------------------------------------------
 | |
| 
 | |
| You can use the ``-k`` command line option to select
 | |
| tests::
 | |
| 
 | |
|     py.test -k webtest  # will only run tests marked as webtest
 | |
| 
 | |
| """
 | |
| import py
 | |
| 
 | |
| def pytest_namespace():
 | |
|     return {'mark': MarkGenerator()}
 | |
| 
 | |
| class MarkGenerator:
 | |
|     """ non-underscore attributes of this object can be used as decorators for 
 | |
|     marking test functions. Example: @py.test.mark.slowtest in front of a 
 | |
|     function will set the 'slowtest' marker object on it. """
 | |
|     def __getattr__(self, name):
 | |
|         if name[0] == "_":
 | |
|             raise AttributeError(name)
 | |
|         return MarkDecorator(name)
 | |
| 
 | |
| class MarkDecorator:
 | |
|     """ decorator for setting function attributes. """
 | |
|     def __init__(self, name):
 | |
|         self.markname = name
 | |
|         self.kwargs = {}
 | |
|         self.args = []
 | |
| 
 | |
|     def __repr__(self):
 | |
|         d = self.__dict__.copy()
 | |
|         name = d.pop('markname')
 | |
|         return "<MarkDecorator %r %r>" %(name, d)
 | |
| 
 | |
|     def __call__(self, *args, **kwargs):
 | |
|         """ if passed a single callable argument: decorate it with mark info. 
 | |
|             otherwise add *args/**kwargs in-place to mark information. """
 | |
|         if args:
 | |
|             func = args[0]
 | |
|             if len(args) == 1 and hasattr(func, '__call__') or \
 | |
|                hasattr(func, '__bases__'):
 | |
|                 if hasattr(func, '__bases__'):
 | |
|                     if hasattr(func, 'pytestmark'):
 | |
|                         l = func.pytestmark
 | |
|                         if not isinstance(l, list):
 | |
|                            func.pytestmark = [l, self]
 | |
|                         else: 
 | |
|                            l.append(self)
 | |
|                     else:
 | |
|                        func.pytestmark = [self]
 | |
|                 else:
 | |
|                     holder = getattr(func, self.markname, None)
 | |
|                     if holder is None:
 | |
|                         holder = MarkInfo(self.markname, self.args, self.kwargs)
 | |
|                         setattr(func, self.markname, holder)
 | |
|                     else:
 | |
|                         holder.kwargs.update(self.kwargs)
 | |
|                         holder.args.extend(self.args)
 | |
|                 return func
 | |
|             else:
 | |
|                 self.args.extend(args)
 | |
|         self.kwargs.update(kwargs)
 | |
|         return self
 | |
|         
 | |
| class MarkInfo:
 | |
|     def __init__(self, name, args, kwargs):
 | |
|         self._name = name
 | |
|         self.args = args
 | |
|         self.kwargs = kwargs
 | |
| 
 | |
|     def __getattr__(self, name):
 | |
|         if name[0] != '_' and name in self.kwargs:
 | |
|             py.log._apiwarn("1.1", "use .kwargs attribute to access key-values")
 | |
|             return self.kwargs[name]
 | |
|         raise AttributeError(name)
 | |
| 
 | |
|     def __repr__(self):
 | |
|         return "<MarkInfo %r args=%r kwargs=%r>" % (
 | |
|                 self._name, self.args, self.kwargs)
 | |
|             
 | |
| 
 | |
| def pytest_pycollect_makeitem(__multicall__, collector, name, obj):
 | |
|     item = __multicall__.execute()
 | |
|     if isinstance(item, py.test.collect.Function):
 | |
|         cls = collector.getparent(py.test.collect.Class)
 | |
|         mod = collector.getparent(py.test.collect.Module)
 | |
|         func = item.obj
 | |
|         func = getattr(func, '__func__', func) # py3
 | |
|         func = getattr(func, 'im_func', func)  # py2
 | |
|         for parent in [x for x in (mod, cls) if x]:
 | |
|             marker = getattr(parent.obj, 'pytestmark', None)
 | |
|             if marker is not None:
 | |
|                 if not isinstance(marker, list):
 | |
|                     marker = [marker]
 | |
|                 for mark in marker:
 | |
|                     if isinstance(mark, MarkDecorator):
 | |
|                         mark(func)
 | |
|                 item.keywords.update(py.builtin._getfuncdict(func) or {})
 | |
|                         
 | |
|     return item
 |