2424replace = ReplacePlugin ()
2525
2626
27- def always (x ):
28- return lambda * args , ** kwargs : x
29-
30-
31- def always_raise (x ):
32- def err (* args , ** kwargs ):
33- raise x
34-
35- return err
36-
37-
3827class TestReplace :
3928 @pytest .fixture
4029 def mp3_file (self , tmp_path ) -> Path :
@@ -76,8 +65,8 @@ def test_run_replace_no_matches(self, library):
7665 def test_run_replace_no_song_selected (
7766 self , library , mp3_file , opus_file , monkeypatch
7867 ):
79- monkeypatch .setattr (replace , "file_check" , always ( None ))
80- monkeypatch .setattr (replace , "select_song" , always ( None ))
68+ monkeypatch .setattr (replace , "file_check" , Mock ( return_value = None ))
69+ monkeypatch .setattr (replace , "select_song" , Mock ( return_value = None ))
8170
8271 item = Item .from_path (mp3_file )
8372 library .add (item )
@@ -90,13 +79,15 @@ def test_run_replace_no_song_selected(
9079 def test_run_replace_not_confirmed (
9180 self , library , mp3_file , opus_file , monkeypatch
9281 ):
93- monkeypatch .setattr (replace , "file_check" , always (None ))
94- monkeypatch .setattr (replace , "confirm_replacement" , always (False ))
82+ monkeypatch .setattr (replace , "file_check" , Mock (return_value = None ))
83+ monkeypatch .setattr (
84+ replace , "confirm_replacement" , Mock (return_value = False )
85+ )
9586
9687 item = Item .from_path (mp3_file )
9788 library .add (item )
9889
99- monkeypatch .setattr (replace , "select_song" , always ( item ))
90+ monkeypatch .setattr (replace , "select_song" , Mock ( return_value = item ))
10091
10192 replace .run (library , optparse .Values (), ["AAA" , str (opus_file )])
10293
@@ -107,13 +98,15 @@ def test_run_replace(self, library, mp3_file, opus_file, monkeypatch):
10798 replace_file = Mock (replace .replace_file , return_value = None )
10899 monkeypatch .setattr (replace , "replace_file" , replace_file )
109100
110- monkeypatch .setattr (replace , "file_check" , always (None ))
111- monkeypatch .setattr (replace , "confirm_replacement" , always (True ))
101+ monkeypatch .setattr (replace , "file_check" , Mock (return_value = None ))
102+ monkeypatch .setattr (
103+ replace , "confirm_replacement" , Mock (return_value = True )
104+ )
112105
113106 item = Item .from_path (mp3_file )
114107 library .add (item )
115108
116- monkeypatch .setattr (replace , "select_song" , always ( item ))
109+ monkeypatch .setattr (replace , "select_song" , Mock ( return_value = item ))
117110
118111 replace .run (library , optparse .Values (), ["AAA" , str (opus_file )])
119112
@@ -136,7 +129,7 @@ def test_path_is_supported_file(self, mp3_file):
136129
137130 def test_select_song_valid_choice (self , monkeypatch , capfd ):
138131 songs = ["Song A" , "Song B" , "Song C" ]
139- monkeypatch .setattr ("builtins.input" , lambda _ : "2" )
132+ monkeypatch .setattr ("builtins.input" , Mock ( return_value = "2" ) )
140133
141134 selected_song = replace .select_song (songs )
142135
@@ -149,26 +142,23 @@ def test_select_song_valid_choice(self, monkeypatch, capfd):
149142
150143 def test_select_song_cancel (self , monkeypatch ):
151144 songs = ["Song A" , "Song B" , "Song C" ]
152- monkeypatch .setattr ("builtins.input" , lambda _ : "0" )
145+ monkeypatch .setattr ("builtins.input" , Mock ( return_value = "0" ) )
153146
154147 selected_song = replace .select_song (songs )
155148
156149 assert selected_song is None
157150
158- def test_select_song_invalid_then_valid (self , monkeypatch , capfd ):
151+ def test_select_song_invalid_then_valid (self , monkeypatch ):
159152 songs = ["Song A" , "Song B" , "Song C" ]
160- inputs = iter (["invalid" , "4" , "3" ])
161- monkeypatch .setattr ("builtins.input" , lambda _ : next (inputs ))
153+ inputs = ["invalid" , "4" , "3" ]
154+ mock_input = Mock (side_effect = iter (inputs ))
155+ monkeypatch .setattr ("builtins.input" , mock_input )
162156
163157 selected_song = replace .select_song (songs )
164158
165- captured = capfd .readouterr ()
166-
167- assert "Invalid input. Please type in a number." in captured .out
168- assert (
169- "Invalid choice. Please enter a number between 1 and 3."
170- in captured .out
171- )
159+ # The first two inputs should be considered invalid, so the third
160+ # input of 3 should be used, resulting in Song C being selected.
161+ assert mock_input .call_count == 3
172162 assert selected_song == "Song C"
173163
174164 def test_confirm_replacement_file_not_exist (self ):
@@ -182,7 +172,7 @@ class Song:
182172
183173 def test_confirm_replacement_yes (self , monkeypatch ):
184174 src = Path (_common .RSRC .decode ()) / "full.mp3"
185- monkeypatch .setattr ("builtins.input" , always ( "yes" ))
175+ monkeypatch .setattr ("builtins.input" , Mock ( return_value = "yes" ))
186176
187177 class Song :
188178 path = str (src ).encode ()
@@ -193,7 +183,7 @@ class Song:
193183
194184 def test_confirm_replacement_no (self , monkeypatch ):
195185 src = Path (_common .RSRC .decode ()) / "full.mp3"
196- monkeypatch .setattr ("builtins.input" , always ( "no" ))
186+ monkeypatch .setattr ("builtins.input" , Mock ( return_value = "no" ))
197187
198188 class Song :
199189 path = str (src ).encode ()
@@ -212,7 +202,8 @@ def test_replace_file_move_fails(self, tmp_path):
212202 def test_replace_file_delete_fails (
213203 self , library , mp3_file , opus_file , monkeypatch
214204 ):
215- monkeypatch .setattr (Path , "unlink" , always_raise (OSError ))
205+ fail_unlink = Mock (side_effect = OSError ("cannot delete" ))
206+ monkeypatch .setattr (Path , "unlink" , fail_unlink )
216207
217208 item = Item .from_path (mp3_file )
218209 library .add (item )
@@ -223,9 +214,8 @@ def test_replace_file_delete_fails(
223214 def test_replace_file_write_fails (
224215 self , library , mp3_file , opus_file , monkeypatch
225216 ):
226- monkeypatch .setattr (
227- Item , "write" , always_raise (WriteError ("path" , "reason" ))
228- )
217+ fail_write = Mock (side_effect = WriteError ("path" , "reason" ))
218+ monkeypatch .setattr (Item , "write" , fail_write )
229219
230220 item = Item .from_path (mp3_file )
231221 library .add (item )
0 commit comments