I wrote a small Python script that will be executed from the command line. After finishing the tests to verify the business logic, I decided to test the command line arguments as well, just to make sure they’re parsed correctly and passed to their appropriate places in the business logic code.
Initially I was thinking I needed to write a test that executes the actual command line such as
python3 myscript.py --argument=value, but as this is neither appropriate nor really necessary for my testing needs, I decided to refactor the production code somewhat to make it testable. More precisely, instead of placing code in inside the
if __name__ == '__main__' block, I decided to refactor the code to look like this:
class Deployer: <snip> def parse_arguments(args): <snip> def main(args): arguments = parse_arguments(args) config_file_path = arguments["config_file_path"] destination_environment = arguments["destination_environment"] version = arguments["version"] application_name = arguments["application"] deployer = Deployer(config_file_path=config_file_path, application_name=application_name, destination_environment=destination_environment, version=version) deployer.deploy() if __name__ == '__main__': main(sys.argv[1:])
Notice that the
main(args) function take care of verifying the script’s arguments, and initialized and start the business logic inside the “Deployer” class.
By placing this initial part of the script’s functionality inside the
main(args) function, it’s much easier to test:
import deploy <snip> @mock.patch("deploy.Deployer", autospec=True) def test_that_command_line_arguments_are_parsed_correctly(self, mocked_deployer): config_file_path = "/path/to/config.json" application_name = "myapp" destination_environment = "production" version = "1.0.0" args_as_string = "--application=" + application_name + \ " --config-file-path=" + config_file_path + \ " --destination-environment=" + destination_environment + \ " --version=" + version args = args_as_string.split(" ") # This is how sys.argv[1:] passes arguments deploy.main(args=args) mocked_deployer.assert_called_once_with(config_file_path=config_file_path, application_name=application_name, destination_environment=destination_environment, version=version) mocked_deployer.return_value.deploy.assert_called_once()