Conversation
Phase 1 of pytest migration: Replace unittest setUp/tearDown with function-scoped autouse fixture. Changes: - Add pytest import - Create test_env autouse fixture that runs for each test - Fixture provides same environment as setUp/tearDown via request.instance - Remove setUp method from TestMain - Remove tearDown method from TestMain - Keep TestCase inheritance (will remove in Phase 2.2) - Keep all test methods unchanged Verification: - All 83 tests pass (83/83) - Tests still use self.tempdir, self.create_env_file, etc. - Fixture handles setup/teardown automatically Why function-scoped not module-level: - Tests create files, repos, modify environment - Need fresh isolation per test - Module-level would share state and break tests Next: Phase 2.1 - Convert assertions to plain assert (keep TestCase)
Phase 2.1 Batch 1: Convert unittest assertions to plain assert for basic main() functionality tests while keeping TestCase base class. Tests converted: - test_main_with_empty_dir_no_files_on_command (no assertions) - test_main_with_emptqy_dir_new_file - test_main_with_empty_git_dir_new_file - test_main_with_empty_git_dir_new_files - test_main_with_dname_and_fname - test_main_with_subdir_repo_fnames - test_main_with_empty_git_dir_new_subdir_file (no assertions) - test_setup_git - test_check_gitignore - test_return_coder - test_main_with_git_config_yml (already had plain assert) Assertions converted: - self.assertTrue(x) → assert x - self.assertFalse(x) → assert not x - self.assertEqual(a, b) → assert a == b - self.assertNotEqual(a, b) → assert a != b (or assert a is not None) - self.assertIsInstance(x, T) → assert isinstance(x, T) Verification: - All 11 tests pass (11/11) - TestCase base class still in place - pytest assertion rewriting tested and working Next: Batch 2 - Environment & configuration tests (20 tests)
Phase 2.1 Batch 2: Convert unittest assertions to plain assert for environment and configuration tests while keeping TestCase base class. Tests converted: - test_env_file_override - test_env_file_flag_sets_automatic_variable - test_default_env_file_sets_automatic_variable - test_false_vals_in_env_file - test_true_vals_in_env_file - test_verbose_mode_lists_env_vars - test_yaml_config_file_loading - test_pytest_env_vars - test_set_env_single - test_set_env_multiple - test_set_env_with_spaces - test_set_env_invalid_format - test_api_key_single - test_api_key_multiple - test_api_key_invalid_format - test_git_config_include - test_git_config_include_directive - test_load_dotenv_files_override Assertions converted: - self.assertEqual(a, b) → assert a == b - self.assertIn(x, y) → assert x in y - self.assertRegex(s, r) → assert re.search(r, s) - self.assertLess(a, b) → assert a < b Verification: - All 18 tests pass (18/18) - TestCase base class still in place Progress: 29/83 tests converted (35%) Next: Batch 3 - Model configuration tests
- test_resolve_aiderignore_path - test_invalid_edit_format - test_default_model_selection - test_model_precedence - test_model_overrides_suffix_applied - test_model_overrides_no_match_preserves_model_name - test_chat_language_spanish - test_commit_language_japanese - test_main_exit_with_git_command_not_found - test_reasoning_effort_option - test_thinking_tokens_option - test_list_models_includes_metadata_models - test_list_models_includes_all_model_sources - test_list_models_with_direct_resource_patch - test_stream_without_cache_no_warning - test_cache_without_stream_no_warning Converted unittest assertions to plain assert statements while keeping TestCase base class. All 83 tests still passing.
- test_command_line_gitignore_files_flag - test_add_command_gitignore_files_flag - test_encodings_arg - test_yes - test_default_yes - test_dark_mode_sets_code_theme - test_light_mode_sets_code_theme - test_lint_option - test_lint_option_with_explicit_files - test_lint_option_with_glob_pattern - test_map_mul_option - test_suggest_shell_commands_default/disabled/enabled - test_detect_urls_default/disabled/enabled - test_accepts_settings_warnings - test_argv_file_respects_git - test_mcp_servers_parsing Converted all remaining unittest assertions to plain assert statements. All 83 tests still passing. Ready to remove TestCase inheritance.
Removed TestCase base class and unittest import. All tests now use pure pytest patterns with plain assert statements and pytest fixtures. All 83 tests passing. Phase 2 (assertion conversion) complete!
Replace 6 separate boolean flag tests with a single parametrized test: - test_suggest_shell_commands_* (3 tests) - test_detect_urls_* (3 tests) Reduces duplication while maintaining all test cases. All 83 tests pass.
Replace 3 separate API key tests with a single parametrized test: - test_api_key_single - test_api_key_multiple - test_api_key_invalid_format All 83 tests pass.
Replace 4 separate --set-env tests with a single parametrized test: - test_set_env_single - test_set_env_multiple - test_set_env_with_spaces - test_set_env_invalid_format All 83 tests pass.
Replace 2 separate mode tests with a single parametrized test: - test_dark_mode_sets_code_theme - test_light_mode_sets_code_theme All 83 tests pass.
Split large test_default_model_selection into: - Parametrized test for 5 API key scenarios (anthropic, deepseek, openrouter, openai, gemini) - Separate test for OAuth fallback when no API keys present All 88 tests pass (83 → 88 due to test expansion).
Replace single test with 5 internal sub-tests with a parametrized test: - test_main_args now tests 5 scenarios via parametrization - no_auto_commits, auto_commits, defaults, no_dirty_commits, dirty_commits All 92 tests pass.
Create dummy_io fixture to eliminate 75+ duplicate DummyInput/Output calls. All tests now use **dummy_io instead of explicit input/output parameters. Reduces duplication and improves test maintainability. All 92 tests pass.
Create mock_coder fixture to eliminate duplicate Coder.create mock setup. Uses pytest-mock's mocker fixture for cleaner mocking. Updated 4 tests to use the new fixture: - test_main_with_git_config_yml - test_main_args (parametrized) - test_false_vals_in_env_file - test_true_vals_in_env_file All 92 tests pass.
Created git_temp_dir fixture to provide temporary git directories: - Added fixture yielding GitTemporaryDirectory as Path object - Added git_temp_dir parameter to 57 test methods that need it - Removed 36 redundant GitTemporaryDirectory() context managers - Tests using self.tempdir (from test_env) excluded from fixture All 92 tests passing.
Created create_env_file factory fixture: - Converted helper method to pytest factory fixture - Uses Path.cwd() to create env files in current test directory - Updated 5 calls across 5 test methods to use fixture - Removed old TestMain.create_env_file method All 92 tests passing.
Converted all 16 @patch decorators to pytest-mock mocker.patch(): - Removed @patch decorators from test methods - Added mocker parameter to test signatures - Replaced unittest.mock.patch with mocker.patch calls - Tests with multiple decorators now use multiple mocker.patch calls All 92 tests passing.
Converted 5 additional `with patch` context managers to pytest-mock: - test_message_file_flag - test_encodings_arg (nested patches) - test_mode_sets_code_theme - test_env_file_flag_sets_automatic_variable - test_default_env_file_sets_automatic_variable Progress: 21/40 total patch usages converted to mocker. All 92 tests passing.
Converted 5 additional `with patch` context managers to pytest-mock: - test_lint_option - test_lint_option_with_explicit_files - test_lint_option_with_glob_pattern - test_map_tokens_option - test_map_tokens_option_with_non_zero_value Progress: 27/40 total patch usages converted to mocker (67.5%). All 92 tests passing.
Converted 2 more `with patch` context managers to pytest-mock: - test_sonnet_and_cache_options (RepoMap) - test_verbose_mode_lists_env_vars (sys.stdout with StringIO) Progress: 29/40 total patch usages converted to mocker (72.5%). All 92 tests passing.
Converted 2 more `with patch` context managers to pytest-mock: - test_invalid_edit_format (sys.stderr with StringIO) - test_list_models_includes_metadata_models (sys.stdout) Progress: 31/40 total patch usages converted (77.5%). All 92 tests passing.
Converted 4 more `with patch` context managers to pytest-mock: - test_list_models_includes_all_model_sources (sys.stdout) - test_check_model_accepts_settings_flag (Model.set_thinking_tokens) - test_list_models_with_direct_resource_patch (3 patches: importlib_resources.files, sys.stdout, Model.set_reasoning_effort) Progress: 36/40 total patch usages converted (90%). All 92 tests passing.
Replace all unittest.mock.patch usage with pytest-mock's mocker: - Converted test_env autouse fixture to use mocker instead of patch - Converted 5 remaining tests using with patch() context managers: - test_main_exit_calls_version_check - test_yaml_config_file_loading - test_accepts_settings_warnings - test_model_overrides_suffix_applied - test_model_overrides_no_match_preserves_model_name - Removed patch import from unittest.mock All 92 tests passing. Code now fully uses pytest-mock for all mocking.
Remove unittest legacy patterns from test_env fixture: - Removed request.instance pattern and all instance variable assignments - Removed request parameter (no longer needed) - Replaced self.tempdir with os.getcwd() in 3 locations: - test_setup_git (2 uses) - test_list_models_with_direct_resource_patch (1 use) - Simplified fixture to only use mocker parameter All 92 tests passing. Fixture is now more idiomatic pytest.
Replace manual environment variable manipulation with pytest's monkeypatch: - test_check_gitignore: Use monkeypatch.setenv for GIT_CONFIG_GLOBAL - test_env_file_override: Use monkeypatch.setenv for HOME and E - test_yaml_config_file_loading: Use monkeypatch.setenv for HOME - test_model_precedence: Use monkeypatch.setenv for API keys Benefits: - Automatic cleanup (no more del os.environ) - More explicit and idiomatic pytest - Cleaner code with fewer lines All 92 tests passing. Phase 3C complete.
Complete transformation to idiomatic pytest: - Removed TestMain class wrapper - Converted all 74 test methods to standalone functions - Removed 'self' parameter from all test signatures - Added comprehensive module docstring - Enhanced test_env fixture documentation Code is now fully idiomatic pytest with: - Function-based tests (no class wrapper) - Pytest fixtures for dependency injection - Parametrized tests for reducing duplication - pytest-mock for all mocking - monkeypatch for environment variables All 92 tests passing.
Merge test_main_smoke.py into test_main.py to eliminate fixture duplication: - Added 2 smoke tests (test_main_executes, test_main_async_executes) - Updated smoke tests to use dummy_io fixture for consistency - Deleted tests/basic/test_main_smoke.py - Updated module docstring to reflect consolidated suite Benefits: - Single source of truth for main() tests - No fixture duplication between files - Simpler test organization Total: 94 tests (92 comprehensive + 2 smoke tests) All tests passing.
Remove test_main_executes as it duplicates existing coverage: - test_main_with_empty_dir_no_files_on_command already tests main() execution - 90+ other tests call main() with various arguments - No unique value added by the redundant smoke test Keep test_main_async_executes as it's the ONLY test for main_async(). Applying clean code principles: avoid test duplication. Total: 93 tests (92 comprehensive + 1 async smoke test) All tests passing.
Remove test_main_async_executes as it provides zero additional coverage: - main() is just a thin wrapper: asyncio.run(main_async(...)) - All 92 tests calling main() already test main_async() indirectly - All business logic lives in main_async(), tested via main() Updated module docstring to clarify that tests cover both entry points. Clean code principle: eliminated all test duplication. Total: 92 comprehensive tests All tests passing.
Add back comments that explain what each test section is validating: - test_use_temperature_settings: Clarify three different scenarios - test_request_timeout_from_extra_params: Explain override behavior These comments provide context that the test names alone don't fully convey, making the tests easier to understand and maintain.
Add back 7 comments that explain test scenarios in test methods: - test_use_temperature_in_send_completion: 3 comments for different scenarios - test_model_override_kwargs: 3 comments for grouping test cases - test_model_override_kwargs_with_existing_extra_params: 1 comment These comments preserve context about what each section tests, making the tests easier to understand without needing to split into separate functions.
Restored explanatory comments in: - test_model_override_kwargs_with_existing_extra_params - test_send_completion_with_override_kwargs These comments improve test readability by clarifying: - Test grouping (override precedence vs nested merging) - What assertions verify (override wins, new param added, etc.) - Test structure (setup, checks, validation) Addresses MR7 discussions Aider-AI#700, Aider-AI#701, Aider-AI#702.
pytest discovers and runs tests automatically without requiring the if __name__ == '__main__' block.
Restore structural comments that explain what is being tested: - test_model_aliases: common aliases vs non-alias - test_parse_token_value: integer, string, k/K suffix, m/M suffix - test_set_thinking_tokens: integer, string, decimal value - test_get_repo_map_tokens: default, boundary, middle range cases - test_configure_model_settings: all model case labels
Resolve conflict in test_scrape.py: - Remove orphaned setUp method (unittest pattern not used in pytest) - Remove orphaned test code fragment from upstream - Remove unused imports (sys, pytest, Commands, InputOutput)
Restore the playwright installation test that was removed during the unittest to pytest migration: - Add pytest fixture for Commands with DummyCoder - Convert test to pytest style with AsyncMock - Update patch paths for new module structure (aider.commands.web) - Verify playwright module import and scraper content return
Fix 16 test failures introduced in merge commit 2641a3f: 1. Add null-check for self.args in check_for_urls (base_coder.py:1644) - Fixes 15 tests that create Coder with args=None 2. Update gpt_prompts test assertion (test_copypaste_coder.py) - gpt_prompts is now a property returning PromptObject, not a class attr - Check behavior (not None, has main_system) instead of identity 3. Simplify tool_call_propagation tests (test_models.py) - Remove incorrect expectations for base_url/custom_llm_provider - These params only apply to custom JSON providers, not standard OpenAI All 613 tests now pass.
- Remove unused imports (Model, pytest, AsyncMock, os) - Fix comparison to None/True/False using 'is' instead of '==' - Fix F631 assertions with trailing commas creating always-true tuples - Apply isort and black formatting
Refator test suite, remove unittest base to avoid the async test case pitfall
…main project folder
Add defensive check for self.args which may not exist when ArchitectCoder is instantiated without full initialization. Use getattr to safely access the attribute and default to None.
- Remove xfail markers from 3 ArchitectCoder tests - Use AsyncMock for async generate() method - Fix assertions to include allow_tweak parameter - Handle SwitchCoder exception raised by reply_completed() - Add missing coder attributes (aider_commit_hashes, etc.) - Remove boilerplate comments
Tests patch AskCoder.__init__ which bypasses normal initialization. Fix the tests to properly set coder.args rather than adding defensive code to production that handles a test-only scenario.
Replace manual __init__ patching with proper Coder.create() factory. This reduces test maintenance burden by letting the coder initialize normally instead of manually setting 10+ attributes.
Production fix:
- io.py: Add missing return after do_run("exit") in EOFError handler
Test fixes:
- Add await for async check_for_file_mentions calls
- Mock confirm_ask to control test behavior
- Pass mock args to Coder.create for realistic test setup
- Remove xfail markers from 6 tests
Two fixes to ensure KeyboardInterrupt properly propagates: 1. In confirm_ask(): Remove asyncio.create_task() wrapper around _confirm_ask(). Using create_task() caused KeyboardInterrupt to be caught by asyncio's event loop rather than propagating normally. 2. In _confirm_ask(): Call prompt_async directly instead of using input_task/get_input(), which catches KeyboardInterrupt and returns "" rather than propagating the exception. This allows callers to detect when users press Ctrl+C during confirmation prompts, preventing unintended default responses.
The xfail markers claimed "Commands.cmd_web method not implemented" but WebCommand was actually registered. The real issues were: 1. Tests didn't pass mock args, causing AttributeError on self.args.yes_always_commands 2. Tests mocked coder.commands.scraper which no longer exists - the scraper is now internal to WebCommand.execute() Fix by: - Passing mock args with yes_always_commands and disable_scraping - Mocking coder.commands.do_run to return expected content - Updating test assertions to verify do_run was called correctly
In get_abs_fnames_content(), deleted files were filtered out for iteration but never removed from self.abs_fnames. This caused stale file references to persist. Now explicitly checks for and removes non-existent files at the start of get_abs_fnames_content(), with a warning message to inform the user.
Fix xfail tests - "args" attribute in ArchitectCoder
Rename primary
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Includes: