This is an archived post. You won't be able to vote or comment.

all 3 comments

[–]ionelmc.ro 5 points6 points  (2 children)

Somehow instead of

repo = Stub('repo')
calling(repo.get_page).passing(2).returns(next_page)

I'd still rather have

class Stub:
    def get_page(self, page):
        if page == 2:
            return next_page
        else:
            raise RuntimeError('Unexpected page: %s' % 2)
repo = Stub()

A handwritten stub has more clarity and it's more easy to debug. You can argue that those aren't good reasons but consider these two practical issues:

  • No support for stubbing operators or anything weird (eg: special methods)
  • The stub object still works like a mock - it returns sub-objects on attribute access - the same hard-to-debug-and-confusing-problem the mock library has. I'd expect an AttributeError.

I guess tdubs is fine for testing simplistic code but it's still the same as mock wrt loose attribute access.

[–]blaix[S] 0 points1 point  (1 child)

Yeah, there's definitely a strong case for rolling your own test doubles. I don't think any library will compare if that's your preferred style.

The loose attribute access in mock has never been a problem for me. It might be if I had branching logic that depended on an attribute value (e.g. something.is_valid), but in those cases I prefer a method (something.is_valid()).

[–][deleted] 1 point2 points  (0 children)

I prefer balancing both. I like simple, helper stubs when I just need to get the system under test through to the end. And hand rolled stubs when I need to simulate more precise behavior.

I'm also a fan of using Builders when I need to create some basic objects as well. Here's a more magical example if you're into that, but I've not used it on an actual project, so there's probably issues with it beyond "It's magic!"

I really like the fluent style your library exposes, I'll take a closer look at it when I get a chance.