Merge ~jugmac00/lpci:easier-testing-for-argparse-applications into lpci:main

Proposed by Jürgen Gmach
Status: Merged
Merged at revision: 2df72a0c6870100e9c194f68711b9f101618802c
Proposed branch: ~jugmac00/lpci:easier-testing-for-argparse-applications
Merge into: lpci:main
Diff against target: 145 lines (+32/-34)
3 files modified
lpcraft/commands/tests/__init__.py (+22/-23)
lpcraft/main.py (+3/-2)
lpcraft/tests/test_main.py (+7/-9)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+412567@code.launchpad.net

Commit message

Enable easier testing for argparse applications

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lpcraft/commands/tests/__init__.py b/lpcraft/commands/tests/__init__.py
2index 86d857d..aba747f 100644
3--- a/lpcraft/commands/tests/__init__.py
4+++ b/lpcraft/commands/tests/__init__.py
5@@ -3,7 +3,7 @@
6
7 from dataclasses import dataclass
8 from typing import List
9-from unittest.mock import ANY, call, patch
10+from unittest.mock import ANY, call
11
12 from craft_cli import CraftError
13 from testtools import TestCase
14@@ -24,25 +24,24 @@ class _CommandResult:
15
16 class CommandBaseTestCase(TestCase):
17 def run_command(self, *args, **kwargs):
18- with patch("sys.argv", ["lpcraft"] + list(args)):
19- with RecordingEmitterFixture() as emitter:
20- exit_code = main()
21- result = _CommandResult(
22- exit_code,
23- [
24- c.args[1]
25- for c in emitter.recorder.interactions
26- if c == call("message", ANY)
27- ],
28- [
29- c.args[1]
30- for c in emitter.recorder.interactions
31- if c == call("error", ANY)
32- ],
33- [
34- c.args[1]
35- for c in emitter.recorder.interactions
36- if c == call("trace", ANY)
37- ],
38- )
39- return result
40+ with RecordingEmitterFixture() as emitter:
41+ exit_code = main(list(args))
42+ result = _CommandResult(
43+ exit_code,
44+ [
45+ c.args[1]
46+ for c in emitter.recorder.interactions
47+ if c == call("message", ANY)
48+ ],
49+ [
50+ c.args[1]
51+ for c in emitter.recorder.interactions
52+ if c == call("error", ANY)
53+ ],
54+ [
55+ c.args[1]
56+ for c in emitter.recorder.interactions
57+ if c == call("trace", ANY)
58+ ],
59+ )
60+ return result
61diff --git a/lpcraft/main.py b/lpcraft/main.py
62index 124be7e..ee4bb7d 100644
63--- a/lpcraft/main.py
64+++ b/lpcraft/main.py
65@@ -6,6 +6,7 @@
66 import logging
67 from argparse import ArgumentParser
68 from pathlib import Path
69+from typing import List, Optional
70
71 from craft_cli import CraftError, EmitterMode, emit
72
73@@ -28,7 +29,7 @@ def _configure_logger(name: str) -> None:
74 _configure_logger("craft_providers")
75
76
77-def main() -> int:
78+def main(argv: Optional[List[str]] = None) -> int:
79 """lpcraft runs Launchpad CI jobs."""
80 parser = ArgumentParser(description="Run Launchpad CI jobs.")
81 parser.add_argument(
82@@ -74,7 +75,7 @@ def main() -> int:
83 emit.init(EmitterMode.NORMAL, "lpcraft", f"Starting {lpcraft_version}")
84
85 try:
86- args = parser.parse_args()
87+ args = parser.parse_args(argv)
88 except SystemExit:
89 emit.ended_ok()
90 return 1
91diff --git a/lpcraft/tests/test_main.py b/lpcraft/tests/test_main.py
92index ee74882..423d7ec 100644
93--- a/lpcraft/tests/test_main.py
94+++ b/lpcraft/tests/test_main.py
95@@ -1,7 +1,6 @@
96 # Copyright 2021 Canonical Ltd. This software is licensed under the
97 # GNU General Public License version 3 (see the file LICENSE).
98
99-import sys
100 from unittest.mock import call, patch
101
102 from craft_cli import CraftError, EmitterMode
103@@ -16,10 +15,9 @@ from lpcraft.tests.fixtures import RecordingEmitterFixture
104 class TestMain(TestCase):
105 def test_ok(self):
106 # main() sets up the message handler and exits cleanly.
107- self.useFixture(MockPatch("sys.argv", ["lpcraft", "--version"]))
108 mock_emit = self.useFixture(MockPatch("lpcraft.main.emit")).mock
109
110- ret = main()
111+ ret = main(["--version"])
112
113 self.assertEqual(0, ret)
114 mock_emit.init.assert_called_once_with(
115@@ -30,24 +28,24 @@ class TestMain(TestCase):
116
117 def test_bad_arguments(self):
118 # main() exits appropriately if given bad arguments.
119- self.useFixture(MockPatch("sys.argv", ["lpcraft", "--nonexistent"]))
120 mock_emit = self.useFixture(MockPatch("lpcraft.main.emit")).mock
121 mock_argparse_print_message = self.useFixture(
122 MockPatch("argparse.ArgumentParser._print_message")
123 ).mock
124
125- ret = main()
126+ ret = main(["--nonexistent"])
127
128 self.assertEqual(1, ret)
129- mock_argparse_print_message.assert_called_with(
130- "lpcraft: error: unrecognized arguments: --nonexistent\n",
131- sys.stderr,
132+ # using `assert_called_with` is not possible as the message is
133+ # different depending whether pytest or coverage is driving the tests
134+ self.assertIn(
135+ "error: unrecognized arguments: --nonexistent\n",
136+ mock_argparse_print_message.call_args.args[0],
137 )
138 mock_emit.ended_ok.assert_called_once_with()
139
140 @patch("lpcraft.main.run")
141 def test_keyboard_interrupt(self, mock_run):
142- self.useFixture(MockPatch("sys.argv", ["lpcraft"]))
143 mock_run.side_effect = KeyboardInterrupt()
144
145 with RecordingEmitterFixture() as emitter:

Subscribers

People subscribed via source and target branches