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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.