[TIP] Multiplatform testing

Michael Foord fuzzyman at voidspace.org.uk
Mon Jan 3 09:34:26 PST 2011


On 03/01/2011 17:13, Manuel de la Pena Saenz wrote:
> Hello *,
>
> At the moment I'm working on the port to Windows of some code that was 
> initially written to work only on Linux (desktopcouch). To make the 
> port easier, I'm currently working on ensuring that the tests that 
> were written can be ran on Windows. This has lead me to a problem I 
> really do not know how to solve:
>
> Desktopcouch uses dbus to advertise the port in which the users 
> personal CouchDb is running. A certain amount of our tests are 
> integration tests that require dbus to be running. As you can imaging 
> this tests fail on Windows but should not be considered to be a fail 
> since it is "an expected fail". To load an run or test I use u1trial, 
> which can be found in Launchpad (launchpad.net/ubuntuone-dev-tools 
> <http://launchpad.net/ubuntuone-dev-tools>) to which I am adding an 
> @skip decorator (and similar) to work with 
> twisted.trial.unittest.TestCase so that we can skip tests (the code 
> can be found at 
> at lp:~mandel/ubuntuone-dev-tools/load_test_according_to_platform in 
> testcase.py). The @skipIf approach works on those tests that do not 
> have imports which cannot be found on Windows (for example, it does 
> not have import dbus), however if the imports are present when loading 
> the test we have an import error and we never get the SkipTest 
> exception. This ImportErrors of course occur due to the fact that 
> u1trial  uses __import__ to load the test cases and the module to load 
> has an "platform illegal import"
>

I solve this problem like this:

import unittest2
try:
     import module
except ImportError:
     module = None

class SomeTest(unittest2.TestCase):
     @unittest2.skipIf(module is None, "Test requires module")
     def test_something(self):
         ...

The skip decorators can be applied to a whole class as well as methods 
from Python 2.6.

An alternative approach:

try:
   import  somemodule
except  ImportError:
   somemodule  =  None


def error_if_missing(name):
   failed = False
   try:
     __import__(name)
   except:
     failed = True
   def  decorator(f):
      def  inner(self):
        if  failed:
          raise  ImportError('name')
        return  f(self)
      return  inner
   return decorator

class  SomeTest(unittest2.TestCase):

   @error_if_missing('somemodule')
   def  test_something2(self):
     ...

I dislike the "auto-adding to module globals" approach as it means a core part of the setup is done behind the scenes and invisible.

All the best,

Michael Foord


> My first attempt to solve this situation has been to write a decorator 
> that will skip a test case if one of its imports fails and if they do 
> not fail, it will add the modules to the func_globals of the test. The 
> code of such an attempt is the following:
>
> def  skipIfNotModules(modules,  reason):
>      """
>      Skip tests when a set of modules is missing.
>      """
>      if  modules:
>          def  decorator(test_item):
>              if  not  (isinstance(test_item,  type)  and\
>                  issubclass(test_item,  TestCase)):
>                  def  decorator(test_item):
>                      @wraps(test_item)
>                      def  import_wrapper(*args,  **kwargs):
>                          for  module_to_import  in  modules:
>                              try:
>                                  test_item.func_globals[module_to_import]  =  __import__(module_to_import)
>                              except  ImportError:
>                                  raise  SkipTest(reason)
>              		return  test_item(*args,  **kwargs)
>              		test_item  =  import_wrapper
>              		return  test_item
>              	return  decorator
>              # tell twisted.trial.unittest to skip the test, pylint will
>              # complain since it thinks we are redefining a name out
>              # of the scope
>              # pylint: disable=W0621,W0612
>              test_item.skip  =  reason
>              # pylint: enable=W0621,W0612
>              # because the item was skipped, we will make sure that no
>              # services are started for it
>              if  hasattr(test_item,  "required_services"):
>                  # pylint: disable=W0612
>                  test_item.required_services  =  lambda  *args,  **kwargs:  []
>                  # pylint: enable=W0612
>              return  test_item
>          return  decorator
>      return  _id
> While the above code works for a test method, it does not for a 
> TestCase. I was wondering if someone in the list has experience with 
> this type of situation in which tests have to be skipped when the are 
> loaded by a test runner like u1trial depending on the platform. I am 
> specially interested in those examples where the information of which 
> test to be run does not have to be supplied to the test loader in an 
> "artificial way" (I consider to be an artificial way to pass a dict 
> with the os.platform as the keys and the value a list of the test to run).
>
> I would greatly appreciate any input in this particular problem as 
> well as to the more general question of, how do I plan my tests so 
> that we can work in a multi-platform env?
>
>
> _______________________________________________
> testing-in-python mailing list
> testing-in-python at lists.idyll.org
> http://lists.idyll.org/listinfo/testing-in-python


-- 
http://www.voidspace.org.uk/

May you do good and not evil
May you find forgiveness for yourself and forgive others
May you share freely, never taking more than you give.
-- the sqlite blessing http://www.sqlite.org/different.html

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.idyll.org/pipermail/testing-in-python/attachments/20110103/23562276/attachment-0001.htm>


More information about the testing-in-python mailing list