Testing Blog

TotT: Better Stubbing in Python

streda, januára 24, 2007
Share on Twitter Share on Facebook
Google
Menovky: TotT

22 komentárov :

  1. William25. januára 2007 o 0:13:00 GMT-8

    Interesting technique. I think the article will be improved if it also mentioned that the technique comes with a cost of adding an extra unintuitive parameter into your code base. All subsequent maintainers of the code will probably find it very confusing to see that the path_checker is a parameter which makes the codebase less maintainable.

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  2. mnot25. januára 2007 o 0:26:00 GMT-8

    Is that really any better? Now the function signature is polluted with arguments for everything that I might want to use this technique for -- which could be considerable.

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  3. hfuecks25. januára 2007 o 0:45:00 GMT-8

    Think pretty much anything in this area quickly turns into an ugly hack but modifying Foo's parameters is probably worse than others.

    A transparent solution can be done via inspecting the stack and checking who the caller is (via python's inspect module) - if it's the function you want to test, using the dummy os.path.exists, otherwise use the real one. Would provide an example but whitespace formatting not supported the the comments here...

    That said surely DoSomething and DoSomethingElse should be stubbed out as well? It's only Foo being tested ;)

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  4. Daniel25. januára 2007 o 2:20:00 GMT-8

    Another possibility is to make the stub check the argument, return a value for 'bar' and call the original function for anything else.

    os.path.exists = lambda x: x == "bar" or old_exists(x)

    Sorry if my code isn't much cop, I don't use Python much.

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  5. Roger Browne25. januára 2007 o 4:56:00 GMT-8

    Cool!

    I don't use python much, but I support the technique of accepting stubbing as a worthwhile evil and designing it into the code. It's not really "polluting" the interface" because it IS the interface.

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  6. My notes25. januára 2007 o 5:52:00 GMT-8

    Hey,

    How about posting an RSS feed for the PDFs? I could hook it up via a script to print them directly to my printer at the office :)

    Cheers

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  7. Unknown25. januára 2007 o 10:08:00 GMT-8

    Is Foo just a helper method for your unit test class? If so, I can see value in the new parameter but if not, it does seem like a hack.

    Full disclosure: I'm pretty new to Python so maybe I'm missing something.

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  8. Nick Parker25. januára 2007 o 10:46:00 GMT-8

    Tento komentár bol odstránený autorom.

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  9. Nick Parker25. januára 2007 o 10:47:00 GMT-8

    Tento komentár bol odstránený autorom.

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  10. Nick Parker25. januára 2007 o 10:50:00 GMT-8

    What about extracting the os.path.exists into a helper that is injected at construction of the class containing Foo? This way your not polluting the parameter list but still providing a separation of concerns.

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  11. Ian Bicking25. januára 2007 o 14:12:00 GMT-8

    I think this issue -- at least the particular one you give -- is a sign of a smell further up in the stack. If you really are doing lots of file operations, you should really write files. The stubbing and mocking you'll have to do to avoid it just isn't worth it, and automatically wiping your scratch test files before the test runs is easy enough as well.

    Once you are comfortable with the testing of the file-related operations, you shouldn't keep testing them. Either stub those out, or just keep using scratch areas for the files.

    Similarly, if you rely on some other external service (the service in this case being the filesystem), putting in a trivial service is often better than futzing with replacing functions that are otherwise quite sufficient. A good example of a tool that allows this is wsgi_intercept, which lets you attach pretend HTTP apps to arbitrary host names. Of course, that's basically the original technique that you wanted to avoid; that someone else did it in a library kind of makes it okay. If you were mocking something that wasn't yet mocked, I'd recommend something more like your technique.

    Putting in a trivial service implementation should just be a matter of configuration. Configuring an address to http://localhost, or configuring the base file path, or pointing to a fake smtp server. You have to do that anyway regardless of testing, and that's a good place for your mocking.

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  12. maciej25. januára 2007 o 16:51:00 GMT-8

    I hope this goes without saying, but in production code the filesystem should be considered untrusted and an assertion about its contents would never be valid. Instead all possible failures should be handled. (Unless you *are* the filesystem.) I am assuming this is just for the sake of example though.

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  13. Marius Gedminas26. januára 2007 o 1:14:00 GMT-8

    I often use class attributes instead of polluting function signatures. There's just one little trick: if you want to assign a function to a class attribute without turning it into a method, you need to wrap it in staticmethod():

    class Whatever(object):
        # hook for unit tests
        path_checker = staticmethod(os.path.exists)

        def foo(self):
            return self.path_checker(self.filename)

    By the way, could you please refrain from inflicting the PEP-8-violating 2-space-indentation internal Google coding style on the rest of the world? Thanks!

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  14. Nelson Biagio Junior26. januára 2007 o 5:15:00 GMT-8

    Very cool and great idea. I'll print this and send to may dev people...

    Regards!

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  15. Eric26. januára 2007 o 5:17:00 GMT-8

    Caution about the python lambda. Word on the street -- it is going away.

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  16. Oisín26. januára 2007 o 8:30:00 GMT-8

    Hey, there's a nicer way of setting a mock object without having it passed in as a parameter.

    At least, it works in C++ and Java - I'm not sure how OO works in Python because I know nothing about it.

    In the class you want to test, say you initialise path_tester = os.file.exists; in a constructor. We replace this with a protected factory method call:
    path_tester = getPathTester();

    Then we implement our protected factory method:
    protected getPathTester() {
    return os.file.exists;
    }


    But when we need to mock it, we create an anonymous inner class which inherits from the original, overriding getPathTester() to return our mock object that says yes or no or whatever (and perhaps, records that it was called x times).
    I found this technique on IBM's developerworks somewhere. Hopefully it makes sense in Python as well...

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  17. Scott26. januára 2007 o 11:02:00 GMT-8

    I use this technique a lot in my tests.

    I avoid having to add code to my production implementation to support the mock objects and expand the mock object to return match paths and to use the underlying implementation as a fall-through case. As a bonus, you can chain multiple mock objects together to mock-out several paths.


    It is pretty trivial to expand my example to take either a list of paths or a function to determine whether the provided path matches.

    My example looks really ugly in the comments, but you can find it on pastebin.com here

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  18. Michael Chen27. januára 2007 o 21:16:00 GMT-8

    That's interesting. Actually, every dynamic language(JavaScript, ActionScript, Ruby, python...) can do that, something like method overriding.

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  19. Michael Chen27. januára 2007 o 21:18:00 GMT-8

    It's interesting. Actually the same trick are among nearlly all dynamic langugages: JavaScript, ActionScript, Python, Ruby, etc. Just a mehtod/property overriding.

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  20. Unknown4. februára 2007 o 12:01:00 GMT-8

    It's a python only technique. For those who don't use python, using optional parameters with default value is never a nice idea. For one parameter, it does a great job, but once you start using two unrelated optionnal value, it's start to be a mess, and optionnal parameters must be avoid.

    def Foo(path, foo_checker=default_foo_checker, bar_checker=default_bar_checker, ):
    # ...
    pass

    With something like that, in C/C++/Java, you can call :
    Foo(path)
    Foo(path,my_foo_checker)
    Foo(path,my_foo_checker,my_bar_checker)

    if you want to pass Foo(path,my_bar_checker), you can't with C/C++/Java, or you need to do :
    Foo(path,default_foo_ckecker,my_bar_checker)

    Erk ? Why would I need to know what is the default foo_checker ? Why would I even need to know that there is a foo_checker argument for another obscur test I don't evn want to know about.

    With pythonic code, you can still write :

    Foo(path)
    Foo(path,my_foo_checker)
    Foo(path,my_foo_checker,my_bar_checker)

    but you can also write :
    Foo(path,foo_checker=my_foo_checker)
    Foo(path,bar_checker=my_bar_checker)
    Foo(path,foo_checker=my_foo_checker,bar_checker=my_bar_checker)

    And even this call works:
    Foo(path,bar_checker=my_bar_checker,foo_checker=my_foo_checker)

    So for all those who don't know python and don't feel easy with optionnal parameters : You are right ! Optionnal parameters in C/C++/Java is not really something to abuse, it's not the same with python.

    That said, I won't have done it that way. I still agree with william, it would still be confusing.

    I would have done it that way :

    First, I would have used an "IOObject" to do that kind of IO things, to be able to change IOComponent.

    Then, I would have created that IO object once (per class or per instance, it depends), and finally, I would have changed that IOObject (with for exemple a child) for the test.

    Note that it's more or less what marius is doing. If you still want to use your optionnal parameter, you can pass IOObject to the constructor.

    To oisin : Yes, it would work the same with python. I still prefer Marius technique (the object is create as a static attribute, and only the test unit can change that attribute, rather than subclassing, because I'm not easy with testing a subclass of the class I really want to test. While I agree it's technically exactly the same)

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  21. elie22. októbra 2014 o 15:06:00 GMT-7

    Why isn't dosomething stubbed out?

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
  22. Abdul Haseeb4. júla 2019 o 0:35:00 GMT-7

    Please link pre-req and sequel episodes/articles, so that beginners like me can first get to know the terms and concepts discussed in suh episodes.

    OdpovedaťOdstrániť
    Odpovede
      Odpovedať
Pridať komentár
Načítať viac...

The comments you read and contribute here belong only to the person who posted them. We reserve the right to remove off-topic comments.

  

Labels


  • TotT 104
  • GTAC 61
  • James Whittaker 42
  • Misko Hevery 32
  • Code Health 31
  • Anthony Vallone 27
  • Patrick Copeland 23
  • Jobs 18
  • Andrew Trenk 13
  • C++ 11
  • Patrik Höglund 8
  • JavaScript 7
  • Allen Hutchison 6
  • George Pirocanac 6
  • Zhanyong Wan 6
  • Harry Robinson 5
  • Java 5
  • Julian Harty 5
  • Adam Bender 4
  • Alberto Savoia 4
  • Ben Yu 4
  • Erik Kuefler 4
  • Philip Zembrod 4
  • Shyam Seshadri 4
  • Chrome 3
  • Dillon Bly 3
  • John Thomas 3
  • Lesley Katzen 3
  • Marc Kaplan 3
  • Markus Clermont 3
  • Max Kanat-Alexander 3
  • Sonal Shah 3
  • APIs 2
  • Abhishek Arya 2
  • Alan Myrvold 2
  • Alek Icev 2
  • Android 2
  • April Fools 2
  • Chaitali Narla 2
  • Chris Lewis 2
  • Chrome OS 2
  • Diego Salas 2
  • Dori Reuveni 2
  • Jason Arbon 2
  • Jochen Wuttke 2
  • Kostya Serebryany 2
  • Marc Eaddy 2
  • Marko Ivanković 2
  • Mobile 2
  • Oliver Chang 2
  • Simon Stewart 2
  • Stefan Kennedy 2
  • Test Flakiness 2
  • Titus Winters 2
  • Tony Voellm 2
  • WebRTC 2
  • Yiming Sun 2
  • Yvette Nameth 2
  • Zuri Kemp 2
  • Aaron Jacobs 1
  • Adam Porter 1
  • Adam Raider 1
  • Adel Saoud 1
  • Alan Faulkner 1
  • Alex Eagle 1
  • Amy Fu 1
  • Anantha Keesara 1
  • Antoine Picard 1
  • App Engine 1
  • Ari Shamash 1
  • Arif Sukoco 1
  • Benjamin Pick 1
  • Bob Nystrom 1
  • Bruce Leban 1
  • Carlos Arguelles 1
  • Carlos Israel Ortiz García 1
  • Cathal Weakliam 1
  • Christopher Semturs 1
  • Clay Murphy 1
  • Dagang Wei 1
  • Dan Maksimovich 1
  • Dan Shi 1
  • Dan Willemsen 1
  • Dave Chen 1
  • Dave Gladfelter 1
  • David Bendory 1
  • David Mandelberg 1
  • Derek Snyder 1
  • Diego Cavalcanti 1
  • Dmitry Vyukov 1
  • Eduardo Bravo Ortiz 1
  • Ekaterina Kamenskaya 1
  • Elliott Karpilovsky 1
  • Elliotte Rusty Harold 1
  • Espresso 1
  • Felipe Sodré 1
  • Francois Aube 1
  • Gene Volovich 1
  • Google+ 1
  • Goran Petrovic 1
  • Goranka Bjedov 1
  • Hank Duan 1
  • Havard Rast Blok 1
  • Hongfei Ding 1
  • Jason Elbaum 1
  • Jason Huggins 1
  • Jay Han 1
  • Jeff Hoy 1
  • Jeff Listfield 1
  • Jessica Tomechak 1
  • Jim Reardon 1
  • Joe Allan Muharsky 1
  • Joel Hynoski 1
  • John Micco 1
  • John Penix 1
  • Jonathan Rockway 1
  • Jonathan Velasquez 1
  • Josh Armour 1
  • Julie Ralph 1
  • Kai Kent 1
  • Kanu Tewary 1
  • Karin Lundberg 1
  • Kaue Silveira 1
  • Kevin Bourrillion 1
  • Kevin Graney 1
  • Kirkland 1
  • Kurt Alfred Kluever 1
  • Manjusha Parvathaneni 1
  • Marek Kiszkis 1
  • Marius Latinis 1
  • Mark Ivey 1
  • Mark Manley 1
  • Mark Striebeck 1
  • Matt Lowrie 1
  • Meredith Whittaker 1
  • Michael Bachman 1
  • Michael Klepikov 1
  • Mike Aizatsky 1
  • Mike Wacker 1
  • Mona El Mahdy 1
  • Noel Yap 1
  • Palak Bansal 1
  • Patricia Legaspi 1
  • Per Jacobsson 1
  • Peter Arrenbrecht 1
  • Peter Spragins 1
  • Phil Norman 1
  • Phil Rollet 1
  • Pooja Gupta 1
  • Project Showcase 1
  • Radoslav Vasilev 1
  • Rajat Dewan 1
  • Rajat Jain 1
  • Rich Martin 1
  • Richard Bustamante 1
  • Roshan Sembacuttiaratchy 1
  • Ruslan Khamitov 1
  • Sam Lee 1
  • Sean Jordan 1
  • Sebastian Dörner 1
  • Sharon Zhou 1
  • Shiva Garg 1
  • Siddartha Janga 1
  • Simran Basi 1
  • Stan Chan 1
  • Stephen Ng 1
  • Tejas Shah 1
  • Test Analytics 1
  • Test Engineer 1
  • Tim Lyakhovetskiy 1
  • Tom O'Neill 1
  • Vojta Jína 1
  • automation 1
  • dead code 1
  • iOS 1
  • mutation testing 1


Archive


  • ►  2025 (1)
    • ►  jan (1)
  • ►  2024 (13)
    • ►  dec (1)
    • ►  okt (1)
    • ►  sep (1)
    • ►  aug (1)
    • ►  júl (1)
    • ►  máj (3)
    • ►  apr (3)
    • ►  mar (1)
    • ►  feb (1)
  • ►  2023 (14)
    • ►  dec (2)
    • ►  nov (2)
    • ►  okt (5)
    • ►  sep (3)
    • ►  aug (1)
    • ►  apr (1)
  • ►  2022 (2)
    • ►  feb (2)
  • ►  2021 (3)
    • ►  jún (1)
    • ►  apr (1)
    • ►  mar (1)
  • ►  2020 (8)
    • ►  dec (2)
    • ►  nov (1)
    • ►  okt (1)
    • ►  aug (2)
    • ►  júl (1)
    • ►  máj (1)
  • ►  2019 (4)
    • ►  dec (1)
    • ►  nov (1)
    • ►  júl (1)
    • ►  jan (1)
  • ►  2018 (7)
    • ►  nov (1)
    • ►  sep (1)
    • ►  júl (1)
    • ►  jún (2)
    • ►  máj (1)
    • ►  feb (1)
  • ►  2017 (17)
    • ►  dec (1)
    • ►  nov (1)
    • ►  okt (1)
    • ►  sep (1)
    • ►  aug (1)
    • ►  júl (2)
    • ►  jún (2)
    • ►  máj (3)
    • ►  apr (2)
    • ►  feb (1)
    • ►  jan (2)
  • ►  2016 (15)
    • ►  dec (1)
    • ►  nov (2)
    • ►  okt (1)
    • ►  sep (2)
    • ►  aug (1)
    • ►  jún (2)
    • ►  máj (3)
    • ►  apr (1)
    • ►  mar (1)
    • ►  feb (1)
  • ►  2015 (14)
    • ►  dec (1)
    • ►  nov (1)
    • ►  okt (2)
    • ►  aug (1)
    • ►  jún (1)
    • ►  máj (2)
    • ►  apr (2)
    • ►  mar (1)
    • ►  feb (1)
    • ►  jan (2)
  • ►  2014 (24)
    • ►  dec (2)
    • ►  nov (1)
    • ►  okt (2)
    • ►  sep (2)
    • ►  aug (2)
    • ►  júl (3)
    • ►  jún (3)
    • ►  máj (2)
    • ►  apr (2)
    • ►  mar (2)
    • ►  feb (1)
    • ►  jan (2)
  • ►  2013 (16)
    • ►  dec (1)
    • ►  nov (1)
    • ►  okt (1)
    • ►  aug (2)
    • ►  júl (1)
    • ►  jún (2)
    • ►  máj (2)
    • ►  apr (2)
    • ►  mar (2)
    • ►  jan (2)
  • ►  2012 (11)
    • ►  dec (1)
    • ►  nov (2)
    • ►  okt (3)
    • ►  sep (1)
    • ►  aug (4)
  • ►  2011 (39)
    • ►  nov (2)
    • ►  okt (5)
    • ►  sep (2)
    • ►  aug (4)
    • ►  júl (2)
    • ►  jún (5)
    • ►  máj (4)
    • ►  apr (3)
    • ►  mar (4)
    • ►  feb (5)
    • ►  jan (3)
  • ►  2010 (37)
    • ►  dec (3)
    • ►  nov (3)
    • ►  okt (4)
    • ►  sep (8)
    • ►  aug (3)
    • ►  júl (3)
    • ►  jún (2)
    • ►  máj (2)
    • ►  apr (3)
    • ►  mar (3)
    • ►  feb (2)
    • ►  jan (1)
  • ►  2009 (54)
    • ►  dec (3)
    • ►  nov (2)
    • ►  okt (3)
    • ►  sep (5)
    • ►  aug (4)
    • ►  júl (15)
    • ►  jún (8)
    • ►  máj (3)
    • ►  apr (2)
    • ►  feb (5)
    • ►  jan (4)
  • ►  2008 (75)
    • ►  dec (6)
    • ►  nov (8)
    • ►  okt (9)
    • ►  sep (8)
    • ►  aug (9)
    • ►  júl (9)
    • ►  jún (6)
    • ►  máj (6)
    • ►  apr (4)
    • ►  mar (4)
    • ►  feb (4)
    • ►  jan (2)
  • ▼  2007 (41)
    • ►  okt (6)
    • ►  sep (5)
    • ►  aug (3)
    • ►  júl (2)
    • ►  jún (2)
    • ►  máj (2)
    • ►  apr (7)
    • ►  mar (5)
    • ►  feb (5)
    • ▼  jan (4)
      • Where are the developer-testers and tester-develop...
      • TotT: Better Stubbing in Python
      • Welcome to the Google Testing Blog
      • Introducing "Testing on the Toilet"

Feed

  • Google
  • Privacy
  • Terms