Programmatically replacing an API for another module

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Tom Plunket

    #1

    Programmatically replacing an API for another module

    I've got a bunch of code that runs under a bunch of unit tests. It'd
    be really handy if when testing I could supply replacement
    functionality to verify that the right things get called without those
    things actually getting called, since frequently those calls take a
    long time to execute and do things that, well, I don't really want to
    do.

    E.g. imagine you've got a routine which takes a filespec, generates a
    list of files, and copies those files from one location to another. (I
    realize that this example could be multiple functions that should be
    tested independently, but please bear with me.) You don't actually
    want to do the file copy, but you want to make sure that the file copy
    API is appropriately called.

    Using win32com, I might then use win32file.CopyF ile, but from testing
    code I'd like to replace that with a different call, a test function
    that I supply. Ideally I could replace the whole module, after
    importing the module under test, I could do something like 'win32com =
    __dict__', and assuming I had a CopyFile function defined in the test
    module, it'd hook right in. However, I'm suspicious that this
    wouldn't actually override the win32com seen by the other module that
    will actually do the execution (and I don't yet understand Python's
    execution model sufficiently).

    At present, ModuleTest.py does 'from Module import *' and then makes
    calls on Module without the "Module." decoration. Since typically a
    file named SomeClass.py will only define the SomeClass class, this is
    more of a convenience than anything. If this is a barrier to
    replacing APIs, though, well, I can change it.

    Any insight is helpful, thanks.


    -tom!
  • Guest's Avatar

    #2
    Re: Programmaticall y replacing an API for another module

    Tom i think i know what you are talking about. If i do then i have recently
    had to deal with something similar.

    One approach could be to create a module that acts as an interface between
    your code and the api whos functions you wish to use.

    The interface would consist of wrapper functions for all of the functions
    you wish to use. This interface will only include the wrappers for the api
    functions you will actually use.

    Now you have created the interface you will need to import the module whose
    functions you want to wrap up in the interface.

    At this point you can either import a stub or the real thing. The code
    snippet would look like this

    try:
    import stub_api as api
    except:
    import real_api as api

    def get():
    return api.get()

    ............... .......etc


    So when you are testing you make sure that the stub_api module is available
    to the interface, the try will succeed and you will call stub functions. If
    you do not make the stub_api available then the try will fail, the exception
    will be handled by importing the real_api. In either case functions will be
    callable by using the api. as you have effectively renamed what is being
    imported to api.

    The advantages of this are that you only make visible to your code the
    fuctions of the api that you are going to use. And you are isolating changes
    to code to the interface only.

    Ok hope this helps.

    Steve





    "Tom Plunket" <tomas@fancy.or gwrote in message
    news:agb4m25v0o 0silbl788qc6jqv k2kran4lv@4ax.c om...
    I've got a bunch of code that runs under a bunch of unit tests. It'd
    be really handy if when testing I could supply replacement
    functionality to verify that the right things get called without those
    things actually getting called, since frequently those calls take a
    long time to execute and do things that, well, I don't really want to
    do.
    >
    E.g. imagine you've got a routine which takes a filespec, generates a
    list of files, and copies those files from one location to another. (I
    realize that this example could be multiple functions that should be
    tested independently, but please bear with me.) You don't actually
    want to do the file copy, but you want to make sure that the file copy
    API is appropriately called.
    >
    Using win32com, I might then use win32file.CopyF ile, but from testing
    code I'd like to replace that with a different call, a test function
    that I supply. Ideally I could replace the whole module, after
    importing the module under test, I could do something like 'win32com =
    __dict__', and assuming I had a CopyFile function defined in the test
    module, it'd hook right in. However, I'm suspicious that this
    wouldn't actually override the win32com seen by the other module that
    will actually do the execution (and I don't yet understand Python's
    execution model sufficiently).
    >
    At present, ModuleTest.py does 'from Module import *' and then makes
    calls on Module without the "Module." decoration. Since typically a
    file named SomeClass.py will only define the SomeClass class, this is
    more of a convenience than anything. If this is a barrier to
    replacing APIs, though, well, I can change it.
    >
    Any insight is helpful, thanks.
    >
    >
    -tom!

    Comment

    • Larry Bates

      #3
      Re: Programmaticall y replacing an API for another module

      Tom Plunket wrote:
      I've got a bunch of code that runs under a bunch of unit tests. It'd
      be really handy if when testing I could supply replacement
      functionality to verify that the right things get called without those
      things actually getting called, since frequently those calls take a
      long time to execute and do things that, well, I don't really want to
      do.
      >
      E.g. imagine you've got a routine which takes a filespec, generates a
      list of files, and copies those files from one location to another. (I
      realize that this example could be multiple functions that should be
      tested independently, but please bear with me.) You don't actually
      want to do the file copy, but you want to make sure that the file copy
      API is appropriately called.
      >
      Using win32com, I might then use win32file.CopyF ile, but from testing
      code I'd like to replace that with a different call, a test function
      that I supply. Ideally I could replace the whole module, after
      importing the module under test, I could do something like 'win32com =
      __dict__', and assuming I had a CopyFile function defined in the test
      module, it'd hook right in. However, I'm suspicious that this
      wouldn't actually override the win32com seen by the other module that
      will actually do the execution (and I don't yet understand Python's
      execution model sufficiently).
      >
      At present, ModuleTest.py does 'from Module import *' and then makes
      calls on Module without the "Module." decoration. Since typically a
      file named SomeClass.py will only define the SomeClass class, this is
      more of a convenience than anything. If this is a barrier to
      replacing APIs, though, well, I can change it.
      >
      Any insight is helpful, thanks.
      >
      >
      -tom!
      Whatever points to something in Python is what is called.

      example

      def abc(a, b):
      z=a+b
      return z

      def efg(a, b):
      z=a*b
      return z


      f=abc
      print f(1,2)
      f=efg
      print f(1,2)

      As long as you implement the full API, you can replace functions
      with others quite easily. Note: this also works with class methods.

      Observation: IMHO it is going to be really hard to keep your functional
      shims up to date as you change your REAL functions/methods. You also
      are testing different code than you are executing which doesn't make
      the unit tests nearly as valuable.

      -Larry

      Comment

      • Tom Plunket

        #4
        Re: Programmaticall y replacing an API for another module

        Larry Bates wrote:
        Whatever points to something in Python is what is called.
        Right.
        f=abc
        print f(1,2)
        f=efg
        print f(1,2)
        Sure. I realized after posting that what I really wanted to ask was
        how to replace an imported module. Steve's earlier response is akin
        to what I'm looking for, although with far too much work. ;)
        As long as you implement the full API, you can replace functions
        with others quite easily. Note: this also works with class methods.
        Right, I can replace a function at a time:

        import os

        def my_join(*args):
        return '/'.join(*args)

        os.path.join = my_join

        ....but I'm wondering if there's a straight-forward way to inject the
        local list of functions into another module.

        What I ran into after posting is that I don't know how to get the
        __dict__ for the module's scope; is it available? I.e. I can do
        'print os.path.__dict_ _', but 'print __dict__' at module scope results
        in a NameError since __dict__ is undefined at that scope. However, if
        I could do 'os.path = local_module' somehow, I'd be getting basically
        what I want. Maybe I could just have a my_os_path.py, and in my test
        code import it as os.path, and then the other code would see that
        os.path is already loaded and not bother doing so again? Haven't
        tried it, am interested in the answer, but I do have enough to work
        with for now... Replacing a module in this way also ensures that I'm
        only calling the parts of the API that I intend to call, which is a
        handy side-effect.
        Observation: IMHO it is going to be really hard to keep your functional
        shims up to date as you change your REAL functions/methods. You also
        are testing different code than you are executing which doesn't make
        the unit tests nearly as valuable.
        Depends on what all I'm mocking. If I'm mocking an expensive service
        across the internet that is time consuming to connect to, or even
        mocking a well-defined system API call that is similarly time
        consuming to call, then I can stub something in that doesn't do
        anything but validate the arguments. Additionally, I can "document"
        the caveats and possibly-errant behavior with my mock, and
        subsequently get automatic testing that I'm dealing with API bugs
        properly.

        These aren't functional shims, which would imply that they sit in
        between two other bits of interesting code under my control. They're
        functional leaves, which are the end point of the operational chain.
        E.g. I queue up all of the commands that I'm sending to the database
        and verify that they're what I intended to send, and I accept that the
        database will interpret those commands as it should.

        Thanks for the feedback, regardless,
        -tom!

        Comment

        • Gabriel Genellina

          #5
          Re: Programmaticall y replacing an API for another module

          At Monday 20/11/2006 21:52, Tom Plunket wrote:
          >What I ran into after posting is that I don't know how to get the
          >__dict__ for the module's scope; is it available? I.e. I can do
          >'print os.path.__dict_ _', but 'print __dict__' at module scope results
          >in a NameError since __dict__ is undefined at that scope. However, if
          >I could do 'os.path = local_module' somehow, I'd be getting basically
          >what I want. Maybe I could just have a my_os_path.py, and in my test
          >code import it as os.path, and then the other code would see that
          >os.path is already loaded and not bother doing so again? Haven't
          >tried it, am interested in the answer, but I do have enough to work
          >with for now... Replacing a module in this way also ensures that I'm
          >only calling the parts of the API that I intend to call, which is a
          >handy side-effect.
          You can do this:
          import my_os_path
          os.path = my_os_path

          That shows also that you can use the module as its own namespace. No
          need to use __dict__ explicitely. dir(module) gives you the names
          defined in the module. From inside the module self, use globals().


          --
          Gabriel Genellina
          Softlab SRL

          _______________ _______________ _______________ _____
          Correo Yahoo!
          Espacio para todos tus mensajes, antivirus y antispam ¡gratis!
          ¡Abrí tu cuenta ya! - http://correo.yahoo.com.ar

          Comment

          • Bruno Desthuilliers

            #6
            Re: Programmaticall y replacing an API for another module

            Tom Plunket wrote:
            I've got a bunch of code that runs under a bunch of unit tests. It'd
            be really handy if when testing I could supply replacement
            functionality to verify that the right things get called without those
            things actually getting called, since frequently those calls take a
            long time to execute and do things that, well, I don't really want to
            do.
            Congratulation, you just rediscovered the MockObject.

            (snip)



            --
            bruno desthuilliers
            python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
            p in 'onurb@xiludom. gro'.split('@')])"

            Comment

            • Larry Bates

              #7
              Re: Programmaticall y replacing an API for another module

              Depends on what all I'm mocking. If I'm mocking an expensive service
              across the internet that is time consuming to connect to, or even
              mocking a well-defined system API call that is similarly time
              consuming to call, then I can stub something in that doesn't do
              anything but validate the arguments. Additionally, I can "document"
              the caveats and possibly-errant behavior with my mock, and
              subsequently get automatic testing that I'm dealing with API bugs
              properly.
              When I find myself doing what you describe, I often introduce
              a "testing" mode switch into the equation. It is normally a
              keyword argument to functions that I then interrogate in the
              function. If I'm running in testing mode, I execute the less
              "expensive" testing code. If I'm not in test mode, I don't.
              There may be better ways, but this has worked well for me.

              -Larry

              Comment

              • Tom Plunket

                #8
                Re: Programmaticall y replacing an API for another module

                Bruno Desthuilliers wrote:
                I've got a bunch of code that runs under a bunch of unit tests. It'd
                be really handy if when testing I could supply replacement
                functionality to verify that the right things get called without those
                things actually getting called, since frequently those calls take a
                long time to execute and do things that, well, I don't really want to
                do.
                >
                Congratulation, you just rediscovered the MockObject.
                How do I rediscover something that I'm explicitly implementing? I had
                set out specifically to mock an API, there was no rediscovery going
                on.

                That does allow me to discover via Google the Python Mock Module,
                though, which may well do what I want:

                from TestedModule import *

                my_win32file = Mock({
                'CopyFile': 'CopyFile called'
                }

                win32file = my_win32file

                ....perhaps.

                I'm just a bit loathe to download and install more stuff when
                something simpler appears to be near-at-hand. ...especially
                considering the page describing this module doesn't offer any download
                links! http://python-mock.sourceforge.net/


                -tom!

                Comment

                • Chris Lambacher

                  #9
                  Re: Programmaticall y replacing an API for another module

                  On Tue, Nov 21, 2006 at 05:00:32PM -0800, Tom Plunket wrote:
                  Bruno Desthuilliers wrote:
                  >
                  I've got a bunch of code that runs under a bunch of unit tests. It'd
                  be really handy if when testing I could supply replacement
                  functionality to verify that the right things get called without those
                  things actually getting called, since frequently those calls take a
                  long time to execute and do things that, well, I don't really want to
                  do.
                  Congratulation, you just rediscovered the MockObject.
                  >
                  How do I rediscover something that I'm explicitly implementing? I had
                  set out specifically to mock an API, there was no rediscovery going
                  on.
                  >
                  That does allow me to discover via Google the Python Mock Module,
                  though, which may well do what I want:
                  >
                  from TestedModule import *
                  >
                  my_win32file = Mock({
                  'CopyFile': 'CopyFile called'
                  }
                  >
                  win32file = my_win32file
                  >
                  ...perhaps.
                  >
                  I'm just a bit loathe to download and install more stuff when
                  something simpler appears to be near-at-hand. ...especially
                  considering the page describing this module doesn't offer any download
                  links! http://python-mock.sourceforge.net/
                  How about mini-mock:
                  http://blog.ianbicking.org/minimock.html

                  Comment

                  • Chris Lambacher

                    #10
                    Re: Programmaticall y replacing an API for another module

                    I'm just a bit loathe to download and install more stuff when
                    something simpler appears to be near-at-hand. ...especially
                    considering the page describing this module doesn't offer any download
                    links! http://python-mock.sourceforge.net/
                    How about mini-mock:
                    http://blog.ianbicking.org/minimock.html
                    Oh yeah, and the download link for python-mock is:
                    http://sourceforge.net/project/showf...roup_id=138804

                    Comment

                    • Tom Plunket

                      #11
                      Re: Programmaticall y replacing an API for another module

                      Chris Lambacher wrote:
                      I'm just a bit loathe to download and install more stuff when
                      something simpler appears to be near-at-hand. ...especially
                      considering the page describing this module doesn't offer any download
                      links! http://python-mock.sourceforge.net/
                      >
                      Oh yeah, and the download link for python-mock is:
                      http://sourceforge.net/project/showf...roup_id=138804
                      Heh, yeah I'm sure that's what I got when I went to sf.net and typed
                      in the project name, then submitted a bug for the homepage that it
                      doesn't offer any links to find the download. ;)

                      -tom!

                      Comment

                      Working...