3

The function below takes password from user. I need to test this using Unit testing/(mocks).

def create_auth():
    username = '{user}'.format(user=getpass.getuser())
    pwd = getpass.getpass()
    auth_string = '{username}:{pwd}'.format(username=username, pwd=pwd)
    return auth_string

i am new to python, any hint would be helpful

Thanks

2 Answers 2

8

Generally speaking, you should always separate user input from program / business logic.

def create_auth_interactive():
    return create_auth(getpass.getuser(), getpass.getpass())

def create_auth(user, pwd):
    username = '{user}'.format(user=user) # do you really need this line??
    auth_string = '{username}:{pwd}'.format(username=username, pwd=pwd)
    return auth_string

class TestCreateAuth(unittest.TestCase):
    def test_create_auth(self):
        self.assertEqual(create_auth('user', 'pass'), 'user:pass')

This separation makes your code more maintainable:

  • your core logic is separate from user input, so you can easily switch where that input comes from in the future (maybe you want to expose your program on a HTTP or a RPC server? Or get input from a GUI instead of a CLI). This follows the loose coupling design principle

  • You can unit test without mocking. Mocking is a very powerful technique but if you find that you're using it a lot, that's a good indication that your code is not following separation of concerns. Take a look on dependency injection

  • It's much easier to construct multiple test cases using a list of input values (but some people prefer to have multiple independent tests, check with your team/project)

Sign up to request clarification or add additional context in comments.

3 Comments

This is excellent, but does this mean in your refactored example one wouldn't unit test the create_auth_interactive ?
@dillahr There is no (meaningful) way to unit test the create_auth_interactive function, since it just does user input. The best you can do is a manual test (with user input) or a end-to-end test (with some fake keyboard input). Note that using a mock here doesn't actually test anything (other than that you're using getpass which is a implementation detail, and usually should not be tested). Ideally you'll need to decouple create_auth_interactive from the rest of the system too - you can create create_auth_hardcoded, and choose which one to use through dependency injection
@loopbackbee So if I do refactor and change the order of the arguments, the unit test won't pick it up? I think there must be a way to test it somehow.
3

Using mock.patch:

class TestCreateAuth(unittest.TestCase):
    @mock.patch('getpass.getpass')
    @mock.patch('getpass.getuser')
    def test_create_auth(self, getuser, getpw):
        getuser.return_value = 'user'
        getpw.return_value = 'pass'
        self.assertEqual(create_auth(), 'user:pass')

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.