adding a simulation mode

Discussion in 'Python' started by andrea crotti, Jul 4, 2012.

  1. I'm writing a program which has to interact with many external
    resources, at least:
    - mysql database
    - perforce
    - shared mounts
    - files on disk

    And the logic is quite complex, because there are many possible paths to
    follow depending on some other parameters.
    This program even needs to run on many virtual machines at the same time
    so the interaction is another thing I need to check...

    Now I successfully managed to mock the database with sqlalchemy and only
    the fields I actually need, but I now would like to simulate also
    everything else.

    I would like for example that if I simulate I can pass a fake database,
    a fake configuration and get the log of what exactly would happen. But
    I'm not sure how to implement it now.. One possibility would be to have
    a global variable (PRETEND_ONLY = False) that if set should be checked
    before every potentially system-dependent command.

    For example

    copytree(src, dest) becomes:
    if not PRETEND_ONLY:
    copytree(src, dest)

    But I don't like it too much because I would have to add a lot of
    garbage around..

    Another way is maybe to set the sys.excepthook to something that catchs
    all the exceptions that would be thrown by these comands, but I'm not
    sure is a good idea either..

    Any suggestion?
     
    andrea crotti, Jul 4, 2012
    #1
    1. Advertising

  2. On Wed, 04 Jul 2012 10:42:56 +0100, andrea crotti wrote:

    > I'm writing a program which has to interact with many external
    > resources, at least:
    > - mysql database
    > - perforce
    > - shared mounts
    > - files on disk
    >
    > And the logic is quite complex, because there are many possible paths to
    > follow depending on some other parameters. This program even needs to
    > run on many virtual machines at the same time so the interaction is
    > another thing I need to check...
    >
    > Now I successfully managed to mock the database with sqlalchemy and only
    > the fields I actually need, but I now would like to simulate also
    > everything else.
    >
    > I would like for example that if I simulate I can pass a fake database,
    > a fake configuration and get the log of what exactly would happen. But
    > I'm not sure how to implement it now.. One possibility would be to have
    > a global variable (PRETEND_ONLY = False) that if set should be checked
    > before every potentially system-dependent command.


    I think a better way would be to use a mock database, etc. For each thing
    which you want to simulate, create a class that has the same interface
    but a simulated implementation.

    Then, have your code accept the thing as an argument.

    E.g. instead of having a hard-coded database connection, allow the
    database connection to be set (perhaps as an argument, perhaps as a
    config option, etc.).

    There are libraries to help with mocks, e.g.:

    http://garybernhardt.github.com/python-mock-comparison/


    > For example
    >
    > copytree(src, dest) becomes:
    > if not PRETEND_ONLY:
    > copytree(src, dest)


    Ewww :(

    Mocking the file system is probably the hardest part, because you
    generally don't have a "FileSystem" object available to be replaced. In
    effect, your program has one giant global variable, the file system.
    Worse, it's not even a named variable, it's hard-coded everywhere you use
    it.

    I don't know of any good solution for that. I've often thought about it,
    but don't have an answer.

    I suppose you could monkey-patch a bunch of stuff:

    if ONLY_PRETEND:
    open = my_mock_open
    copytree = my_mock_copytree
    # etc.
    main() # run your application



    but that would also be painful.


    --
    Steven
     
    Steven D'Aprano, Jul 4, 2012
    #2
    1. Advertising

  3. 2012/7/4 Steven D'Aprano <>:
    >
    > Then, have your code accept the thing as an argument.
    >
    > E.g. instead of having a hard-coded database connection, allow the
    > database connection to be set (perhaps as an argument, perhaps as a
    > config option, etc.).
    >
    > There are libraries to help with mocks, e.g.:
    >
    > http://garybernhardt.github.com/python-mock-comparison/


    Ah yes this part is already done, I pass an object to the entry point
    of the program which represents the database connection, which looks
    like this:

    class MockMysqlEngine(MySqlEngine):
    # TODO: make the engine more generic would avoid this dirty hack
    def __init__(self, *args, **kwargs):
    # self.engine =
    create_engine('sqlite:////home/andrea/testdb.sqlite', echo=True)
    self.engine = create_engine('sqlite://', echo=True)
    self.meta = MetaData(bind=self.engine)
    self.session_maker = sessionmaker(bind=self.engine)


    Now I populate statically the schema and populate with some test data
    too, but I'm also implementing a weay to just pass some CSV files so
    that other people can easily write some test cases with some other
    possible database configurations.

    (And I use mock for a few other things)


    >
    >
    >> For example
    >>
    >> copytree(src, dest) becomes:
    >> if not PRETEND_ONLY:
    >> copytree(src, dest)

    >
    > Ewww :(
    >
    > Mocking the file system is probably the hardest part, because you
    > generally don't have a "FileSystem" object available to be replaced. In
    > effect, your program has one giant global variable, the file system.
    > Worse, it's not even a named variable, it's hard-coded everywhere you use
    > it.
    >
    > I don't know of any good solution for that. I've often thought about it,
    > but don't have an answer.
    >
    > I suppose you could monkey-patch a bunch of stuff:
    >
    > if ONLY_PRETEND:
    > open = my_mock_open
    > copytree = my_mock_copytree
    > # etc.
    > main() # run your application
    >
    >
    >
    > but that would also be painful.
    >


    Yes there is no easy solution apparently.. But I'm also playing
    around with vagrant and virtual machine generations, suppose I'm able
    to really control what will be on the machine at time X, creating it
    on demand with what I need, it might be a good way to solve my
    problems (a bit overkill and slow maybe though).

    I'll try the sys.excepthook trick first, any error should give me an
    exception, so if I catch them all I think it might work already..
     
    andrea crotti, Jul 4, 2012
    #3
  4. > Yes there is no easy solution apparently.. But I'm also playing
    > around with vagrant and virtual machine generations, suppose I'm able
    > to really control what will be on the machine at time X, creating it
    > on demand with what I need, it might be a good way to solve my
    > problems (a bit overkill and slow maybe though).
    >
    > I'll try the sys.excepthook trick first, any error should give me an
    > exception, so if I catch them all I think it might work already..



    I actually thought that the sys.excepthook would be easy but it's not
    so trivial apparently:
    This simple sample never reaches the print("here"), because even if
    the exception is catched it still quits with return code=1.

    I also tried to catch the signal but same result, how do I make it
    continue and just don't complain?

    The other option if of course to do a big try/except, but I would
    prefer the excepthook solution..


    import sys
    from shutil import copy

    def my_except_hook(etype, value, tb):
    print("got an exception of type", etype)


    if __name__ == '__main__':
    sys.excepthook = my_except_hook
    copy('sdflsdk')
    print("here")
     
    andrea crotti, Jul 4, 2012
    #4
  5. andrea crotti

    Paul Rubin Guest

    andrea crotti <> writes:
    > copytree(src, dest) becomes:
    > if not PRETEND_ONLY:
    > copytree(src, dest)
    >
    > But I don't like it too much because I would have to add a lot of
    > garbage around..


    I've had good results writing the module under test in the style of a
    java applet, i.e. one of its args is a class instance representing the
    "outside world", and ALL interaction that you might want to simulate is
    done through this object:

    def your_prog(parent):
    conn = parent.db.make_connection(...)
    blah = parent.copytree(...)

    Then you make "real" and "mock" versions of the external interface, and
    pass in an appropriate instance.
     
    Paul Rubin, Jul 4, 2012
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Mike C. Fletcher

    Re: adding a simulation mode

    Mike C. Fletcher, Jul 4, 2012, in forum: Python
    Replies:
    0
    Views:
    177
    Mike C. Fletcher
    Jul 4, 2012
  2. Devin Jeanpierre

    Re: adding a simulation mode

    Devin Jeanpierre, Jul 4, 2012, in forum: Python
    Replies:
    0
    Views:
    196
    Devin Jeanpierre
    Jul 4, 2012
  3. Dieter Maurer

    Re: adding a simulation mode

    Dieter Maurer, Jul 5, 2012, in forum: Python
    Replies:
    0
    Views:
    177
    Dieter Maurer
    Jul 5, 2012
  4. andrea crotti

    Re: adding a simulation mode

    andrea crotti, Jul 5, 2012, in forum: Python
    Replies:
    0
    Views:
    169
    andrea crotti
    Jul 5, 2012
  5. andrea crotti

    Re: adding a simulation mode

    andrea crotti, Jul 12, 2012, in forum: Python
    Replies:
    11
    Views:
    423
    Chris Angelico
    Jul 13, 2012
Loading...

Share This Page