← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~jugmac00/lpcraft:easier-testing-for-argparse-applications into lpcraft:main

 

Jürgen Gmach has proposed merging ~jugmac00/lpcraft:easier-testing-for-argparse-applications into lpcraft:main.

Commit message:
Enable easier testing for argparse applications

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~jugmac00/lpcraft/+git/lpcraft/+merge/412567
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~jugmac00/lpcraft:easier-testing-for-argparse-applications into lpcraft:main.
diff --git a/lpcraft/commands/tests/__init__.py b/lpcraft/commands/tests/__init__.py
index 86d857d..aba747f 100644
--- a/lpcraft/commands/tests/__init__.py
+++ b/lpcraft/commands/tests/__init__.py
@@ -3,7 +3,7 @@
 
 from dataclasses import dataclass
 from typing import List
-from unittest.mock import ANY, call, patch
+from unittest.mock import ANY, call
 
 from craft_cli import CraftError
 from testtools import TestCase
@@ -24,25 +24,24 @@ class _CommandResult:
 
 class CommandBaseTestCase(TestCase):
     def run_command(self, *args, **kwargs):
-        with patch("sys.argv", ["lpcraft"] + list(args)):
-            with RecordingEmitterFixture() as emitter:
-                exit_code = main()
-                result = _CommandResult(
-                    exit_code,
-                    [
-                        c.args[1]
-                        for c in emitter.recorder.interactions
-                        if c == call("message", ANY)
-                    ],
-                    [
-                        c.args[1]
-                        for c in emitter.recorder.interactions
-                        if c == call("error", ANY)
-                    ],
-                    [
-                        c.args[1]
-                        for c in emitter.recorder.interactions
-                        if c == call("trace", ANY)
-                    ],
-                )
-                return result
+        with RecordingEmitterFixture() as emitter:
+            exit_code = main(list(args))
+            result = _CommandResult(
+                exit_code,
+                [
+                    c.args[1]
+                    for c in emitter.recorder.interactions
+                    if c == call("message", ANY)
+                ],
+                [
+                    c.args[1]
+                    for c in emitter.recorder.interactions
+                    if c == call("error", ANY)
+                ],
+                [
+                    c.args[1]
+                    for c in emitter.recorder.interactions
+                    if c == call("trace", ANY)
+                ],
+            )
+            return result
diff --git a/lpcraft/main.py b/lpcraft/main.py
index 124be7e..4b8cd18 100644
--- a/lpcraft/main.py
+++ b/lpcraft/main.py
@@ -4,8 +4,10 @@
 """Main entry point."""
 
 import logging
+import sys
 from argparse import ArgumentParser
 from pathlib import Path
+from typing import List, Optional
 
 from craft_cli import CraftError, EmitterMode, emit
 
@@ -28,7 +30,7 @@ def _configure_logger(name: str) -> None:
 _configure_logger("craft_providers")
 
 
-def main() -> int:
+def main(argv: Optional[List[str]] = None) -> int:
     """lpcraft runs Launchpad CI jobs."""
     parser = ArgumentParser(description="Run Launchpad CI jobs.")
     parser.add_argument(
@@ -73,8 +75,11 @@ def main() -> int:
 
     emit.init(EmitterMode.NORMAL, "lpcraft", f"Starting {lpcraft_version}")
 
+    if argv is None:
+        argv = sys.argv[1:]
+
     try:
-        args = parser.parse_args()
+        args = parser.parse_args(argv)
     except SystemExit:
         emit.ended_ok()
         return 1
diff --git a/lpcraft/tests/test_main.py b/lpcraft/tests/test_main.py
index e8808e5..deeb8b1 100644
--- a/lpcraft/tests/test_main.py
+++ b/lpcraft/tests/test_main.py
@@ -1,7 +1,6 @@
 # Copyright 2021 Canonical Ltd.  This software is licensed under the
 # GNU General Public License version 3 (see the file LICENSE).
 
-import sys
 from unittest.mock import patch
 
 from craft_cli import EmitterMode
@@ -16,10 +15,9 @@ from lpcraft.tests.fixtures import RecordingEmitterFixture
 class TestMain(TestCase):
     def test_ok(self):
         # main() sets up the message handler and exits cleanly.
-        self.useFixture(MockPatch("sys.argv", ["lpcraft", "--version"]))
         mock_emit = self.useFixture(MockPatch("lpcraft.main.emit")).mock
 
-        ret = main()
+        ret = main(["--version"])
 
         self.assertEqual(0, ret)
         mock_emit.init.assert_called_once_with(
@@ -30,24 +28,24 @@ class TestMain(TestCase):
 
     def test_bad_arguments(self):
         # main() exits appropriately if given bad arguments.
-        self.useFixture(MockPatch("sys.argv", ["lpcraft", "--nonexistent"]))
         mock_emit = self.useFixture(MockPatch("lpcraft.main.emit")).mock
         mock_argparse_print_message = self.useFixture(
             MockPatch("argparse.ArgumentParser._print_message")
         ).mock
 
-        ret = main()
+        ret = main(["--nonexistent"])
 
         self.assertEqual(1, ret)
-        mock_argparse_print_message.assert_called_with(
-            "lpcraft: error: unrecognized arguments: --nonexistent\n",
-            sys.stderr,
+        # using `assert_called_with` is not possible as the message is
+        # different depending whether pytest or coverage is driving the tests
+        self.assertIn(
+            "error: unrecognized arguments: --nonexistent\n",
+            mock_argparse_print_message.call_args.args[0],
         )
         mock_emit.ended_ok.assert_called_once_with()
 
     @patch("lpcraft.main.run")
     def test_keyboard_interrupt(self, mock_run):
-        self.useFixture(MockPatch("sys.argv", ["lpcraft"]))
         mock_run.side_effect = KeyboardInterrupt()
 
         with RecordingEmitterFixture() as emitter: