From 18d60340ce9e8717b744b8cc13cde19f142bbc1f Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 4 Feb 2026 14:22:11 +0800 Subject: [PATCH] test: use match in pytest.raises --- tests/commands/test_bump_command.py | 68 +++++++++++----------- tests/commands/test_changelog_command.py | 4 +- tests/commands/test_check_command.py | 40 ++++++------- tests/commands/test_commit_command.py | 16 ++--- tests/test_bump_update_version_in_files.py | 33 ++++++----- tests/test_changelog.py | 11 ++-- tests/test_cli.py | 34 +++++------ tests/test_conf.py | 10 ++-- tests/test_cz_customize.py | 8 ++- tests/test_factory.py | 8 ++- tests/test_git.py | 3 +- 11 files changed, 114 insertions(+), 121 deletions(-) diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index 1e5a162ba..b26f095c9 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -314,9 +314,8 @@ def test_bump_tag_exists_raises_exception(util: UtilFixture): util.create_file_and_commit("feat: new file") git.tag("0.2.0") - with pytest.raises(BumpTagFailedError) as excinfo: + with pytest.raises(BumpTagFailedError, match=re.escape("0.2.0")): util.run_cli("bump", "--yes") - assert "0.2.0" in str(excinfo.value) # This should be a fatal error @pytest.mark.usefixtures("tmp_commitizen_project") @@ -338,18 +337,17 @@ def test_bump_when_bumping_is_not_support(util: UtilFixture): "feat: new user interface\n\nBREAKING CHANGE: age is no longer supported" ) - with pytest.raises(NoPatternMapError) as excinfo: + with pytest.raises(NoPatternMapError, match="'cz_jira' rule does not support bump"): util.run_cli("-n", "cz_jira", "bump", "--yes") - assert "'cz_jira' rule does not support bump" in str(excinfo.value) @pytest.mark.usefixtures("tmp_git_project") def test_bump_when_version_is_not_specify(util: UtilFixture): - with pytest.raises(NoVersionSpecifiedError) as excinfo: + with pytest.raises( + NoVersionSpecifiedError, match=re.escape(NoVersionSpecifiedError.message) + ): util.run_cli("bump") - assert NoVersionSpecifiedError.message in str(excinfo.value) - @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_when_no_new_commit(util: UtilFixture): @@ -360,11 +358,11 @@ def test_bump_when_no_new_commit(util: UtilFixture): util.run_cli("bump", "--yes") # bump without a new commit. - with pytest.raises(NoCommitsFoundError) as excinfo: + with pytest.raises( + NoCommitsFoundError, match=r"\[NO_COMMITS_FOUND\]\nNo new commits found\." + ): util.run_cli("bump", "--yes") - assert "[NO_COMMITS_FOUND]\nNo new commits found." in str(excinfo.value) - def test_bump_when_version_inconsistent_in_version_files( tmp_commitizen_project, util: UtilFixture @@ -378,11 +376,12 @@ def test_bump_when_version_inconsistent_in_version_files( util.create_file_and_commit("feat: new file") - with pytest.raises(CurrentVersionNotFoundError) as excinfo: + with pytest.raises( + CurrentVersionNotFoundError, + match=re.escape("Current version 0.1.0 is not found in"), + ): util.run_cli("bump", "--yes", "--check-consistency") - assert "Current version 0.1.0 is not found in" in str(excinfo.value) - def test_bump_major_version_zero_when_major_is_not_zero( tmp_commitizen_project, util: UtilFixture @@ -403,13 +402,14 @@ def test_bump_major_version_zero_when_major_is_not_zero( util.create_tag("v1.0.0") util.create_file_and_commit("feat(user)!: new file") - with pytest.raises(NotAllowed) as excinfo: + with pytest.raises( + NotAllowed, + match=re.escape( + "--major-version-zero is meaningless for current version 1.0.0" + ), + ): util.run_cli("bump", "--yes", "--major-version-zero") - assert "--major-version-zero is meaningless for current version 1.0.0" in str( - excinfo.value - ) - def test_bump_files_only(tmp_commitizen_project, util: UtilFixture): tmp_version_file = tmp_commitizen_project / "__version__.py" @@ -539,13 +539,14 @@ def test_prevent_prerelease_when_no_increment_detected( util.create_file_and_commit("test: new file") - with pytest.raises(NoCommitsFoundError) as excinfo: + with pytest.raises( + NoCommitsFoundError, + match=re.escape( + "[NO_COMMITS_FOUND]\nNo commits found to generate a pre-release." + ), + ): util.run_cli("bump", "-pr", "beta") - assert "[NO_COMMITS_FOUND]\nNo commits found to generate a pre-release." in str( - excinfo.value - ) - @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_with_changelog_to_stdout_arg( @@ -735,14 +736,14 @@ def test_bump_invalid_manual_version_raises_exception( ): util.create_file_and_commit("feat: new file") - with pytest.raises(InvalidManualVersion) as excinfo: + with pytest.raises( + InvalidManualVersion, + match=re.escape( + f"[INVALID_MANUAL_VERSION]\nInvalid manual version: '{manual_version}'" + ), + ): util.run_cli("bump", "--yes", manual_version) - assert ( - f"[INVALID_MANUAL_VERSION]\nInvalid manual version: '{manual_version}'" - in str(excinfo.value) - ) - @pytest.mark.usefixtures("tmp_commitizen_project") @pytest.mark.parametrize( @@ -770,13 +771,12 @@ def test_bump_manual_version(util: UtilFixture, manual_version): @pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_manual_version_disallows_major_version_zero(util: UtilFixture): util.create_file_and_commit("feat: new file") - with pytest.raises(NotAllowed) as excinfo: + with pytest.raises( + NotAllowed, + match="--major-version-zero cannot be combined with MANUAL_VERSION", + ): util.run_cli("bump", "--yes", "--major-version-zero", "0.2.0") - assert "--major-version-zero cannot be combined with MANUAL_VERSION" in str( - excinfo.value - ) - @pytest.mark.parametrize( ("initial_version", "expected_version_after_bump"), diff --git a/tests/commands/test_changelog_command.py b/tests/commands/test_changelog_command.py index b2d024ac7..469f9e88e 100644 --- a/tests/commands/test_changelog_command.py +++ b/tests/commands/test_changelog_command.py @@ -861,11 +861,9 @@ def test_changelog_from_rev_range_not_found( util.create_file_and_commit("feat: new file") util.create_tag("1.0.0") - with pytest.raises(NoCommitsFoundError) as excinfo: + with pytest.raises(NoCommitsFoundError, match="Could not find a valid revision"): util.run_cli("changelog", rev_range) # it shouldn't exist - assert "Could not find a valid revision" in str(excinfo) - @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_multiple_matching_tags( diff --git a/tests/commands/test_check_command.py b/tests/commands/test_check_command.py index f3f860313..d587fc070 100644 --- a/tests/commands/test_check_command.py +++ b/tests/commands/test_check_command.py @@ -73,9 +73,8 @@ def test_check_jira_fails(mocker: MockFixture, util: UtilFixture): mock_path.return_value.read_text.return_value = ( "random message for J-2 #fake_command blah" ) - with pytest.raises(InvalidCommitMessageError) as excinfo: + with pytest.raises(InvalidCommitMessageError, match="commit validation: failed!"): util.run_cli("-n", "cz_jira", "check", "--commit-msg-file", "some_file") - assert "commit validation: failed!" in str(excinfo.value) @pytest.mark.parametrize( @@ -167,30 +166,31 @@ def test_check_a_range_of_git_commits_and_failed(config, mocker: MockFixture): return_value=_build_fake_git_commits(["This commit does not follow rule"]), ) - with pytest.raises(InvalidCommitMessageError) as excinfo: + with pytest.raises( + InvalidCommitMessageError, match="This commit does not follow rule" + ): commands.Check(config=config, arguments={"rev_range": "HEAD~10..master"})() - assert "This commit does not follow rule" in str(excinfo.value) def test_check_command_with_invalid_argument(config): - with pytest.raises(InvalidCommandArgumentError) as excinfo: + with pytest.raises( + InvalidCommandArgumentError, + match="Only one of --rev-range, --message, and --commit-msg-file is permitted by check command!", + ): commands.Check( config=config, arguments={"commit_msg_file": "some_file", "rev_range": "HEAD~10..master"}, ) - assert ( - "Only one of --rev-range, --message, and --commit-msg-file is permitted by check command!" - in str(excinfo.value) - ) @pytest.mark.usefixtures("tmp_commitizen_project") def test_check_command_with_empty_range(config: BaseConfig, util: UtilFixture): # must initialize git with a commit util.create_file_and_commit("feat: initial") - with pytest.raises(NoCommitsFoundError) as excinfo: + with pytest.raises( + NoCommitsFoundError, match="No commit found with range: 'master..master'" + ): commands.Check(config=config, arguments={"rev_range": "master..master"})() - assert "No commit found with range: 'master..master'" in str(excinfo) def test_check_a_range_of_failed_git_commits(config, mocker: MockFixture): @@ -204,9 +204,11 @@ def test_check_a_range_of_failed_git_commits(config, mocker: MockFixture): return_value=_build_fake_git_commits(ill_formatted_commits_msgs), ) - with pytest.raises(InvalidCommitMessageError) as excinfo: + with pytest.raises( + InvalidCommitMessageError, + match=r"[\s\S]*".join(ill_formatted_commits_msgs), + ): commands.Check(config=config, arguments={"rev_range": "HEAD~10..master"})() - assert all([msg in str(excinfo.value) for msg in ill_formatted_commits_msgs]) def test_check_command_with_valid_message(config, success_mock: MockType): @@ -277,9 +279,8 @@ def test_check_command_with_pipe_message_and_failed( ): mocker.patch("sys.stdin", StringIO("bad commit message")) - with pytest.raises(InvalidCommitMessageError) as excinfo: + with pytest.raises(InvalidCommitMessageError, match="commit validation: failed!"): util.run_cli("check") - assert "commit validation: failed!" in str(excinfo.value) def test_check_command_with_comment_in_message_file( @@ -440,11 +441,10 @@ def test_check_command_with_custom_validator_failed( mock_path.return_value.read_text.return_value = ( "123-ABC issue id has wrong format and misses colon" ) - with pytest.raises(InvalidCommitMessageError) as excinfo: + with pytest.raises( + InvalidCommitMessageError, + match=r"commit validation: failed![\s\S]*pattern: ", + ): util.run_cli( "--name", "cz_custom_validator", "check", "--commit-msg-file", "some_file" ) - assert "commit validation: failed!" in str(excinfo.value), ( - "Pattern validation unexpectedly passed" - ) - assert "pattern: " in str(excinfo.value), "Pattern not found in error message" diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 292530a8d..247419019 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -1,3 +1,4 @@ +import re import sys from pathlib import Path from unittest.mock import ANY @@ -84,11 +85,11 @@ def test_commit_backup_on_failure( @pytest.mark.usefixtures("staging_is_clean", "commit_mock") def test_commit_retry_fails_no_backup(config): - with pytest.raises(NoCommitBackupError) as excinfo: + with pytest.raises( + NoCommitBackupError, match=re.escape(NoCommitBackupError.message) + ): commands.Commit(config, {"retry": True})() - assert NoCommitBackupError.message in str(excinfo.value) - @pytest.mark.usefixtures("staging_is_clean", "backup_file") def test_commit_retry_works( @@ -209,11 +210,9 @@ def test_commit_command_with_gpgsign_and_always_signoff_enabled( def test_commit_when_nothing_to_commit(config, mocker: MockFixture): mocker.patch("commitizen.git.is_staging_clean", return_value=True) - with pytest.raises(NothingToCommitError) as excinfo: + with pytest.raises(NothingToCommitError, match="No files added to staging!"): commands.Commit(config, {})() - assert "No files added to staging!" in str(excinfo.value) - @pytest.mark.usefixtures("staging_is_clean", "prompt_mock_feat") def test_commit_with_allow_empty(config, success_mock: MockType, commit_mock: MockType): @@ -242,12 +241,9 @@ def test_commit_when_customized_expected_raised(config, mocker: MockFixture): _err = ValueError() _err.__context__ = CzException("This is the root custom err") mocker.patch("questionary.prompt", side_effect=_err) - with pytest.raises(CustomError) as excinfo: + with pytest.raises(CustomError, match="This is the root custom err"): commands.Commit(config, {})() - # Assert only the content in the formatted text - assert "This is the root custom err" in str(excinfo.value) - @pytest.mark.usefixtures("staging_is_clean") def test_commit_when_non_customized_expected_raised(config, mocker: MockFixture): diff --git a/tests/test_bump_update_version_in_files.py b/tests/test_bump_update_version_in_files.py index 80823a4e1..d52725b36 100644 --- a/tests/test_bump_update_version_in_files.py +++ b/tests/test_bump_update_version_in_files.py @@ -1,3 +1,4 @@ +import re from collections.abc import Callable from pathlib import Path from shutil import copyfile @@ -199,7 +200,14 @@ def test_file_version_inconsistent_error( ] old_version = "1.2.3" new_version = "2.0.0" - with pytest.raises(CurrentVersionNotFoundError) as excinfo: + with pytest.raises( + CurrentVersionNotFoundError, + match=re.escape( + f"Current version {old_version} is not found in {inconsistent_python_version_file}.\n" + "The version defined in commitizen configuration and the ones in " + "version_files are possibly inconsistent." + ), + ): bump.update_version_in_files( old_version, new_version, @@ -208,13 +216,6 @@ def test_file_version_inconsistent_error( encoding="utf-8", ) - expected_msg = ( - f"Current version 1.2.3 is not found in {inconsistent_python_version_file}.\n" - "The version defined in commitizen configuration and the ones in " - "version_files are possibly inconsistent." - ) - assert expected_msg in str(excinfo.value) - def test_multiple_versions_to_bump( multiple_versions_to_update_poetry_lock, file_regression @@ -285,7 +286,14 @@ def test_update_version_in_files_with_check_consistency_true_failure( version_files = [commitizen_config_file, inconsistent_python_version_file] # This should fail because inconsistent_python_version_file doesn't contain the current version - with pytest.raises(CurrentVersionNotFoundError) as excinfo: + with pytest.raises( + CurrentVersionNotFoundError, + match=re.escape( + f"Current version {old_version} is not found in {inconsistent_python_version_file}.\n" + "The version defined in commitizen configuration and the ones in " + "version_files are possibly inconsistent." + ), + ): bump.update_version_in_files( old_version, new_version, @@ -294,13 +302,6 @@ def test_update_version_in_files_with_check_consistency_true_failure( encoding="utf-8", ) - expected_msg = ( - f"Current version {old_version} is not found in {inconsistent_python_version_file}.\n" - "The version defined in commitizen configuration and the ones in " - "version_files are possibly inconsistent." - ) - assert expected_msg in str(excinfo.value) - @pytest.mark.parametrize( ("encoding", "filename"), diff --git a/tests/test_changelog.py b/tests/test_changelog.py index 11c3a6044..24ec3df97 100644 --- a/tests/test_changelog.py +++ b/tests/test_changelog.py @@ -1228,11 +1228,11 @@ def test_generate_ordered_changelog_tree(change_type_order, expected_reordering) def test_generate_ordered_changelog_tree_raises(): change_type_order = ["BREAKING CHANGE", "feat", "refactor", "feat"] - with pytest.raises(InvalidConfigurationError) as excinfo: + with pytest.raises( + InvalidConfigurationError, match="Change types contain duplicated types" + ): list(changelog.generate_ordered_changelog_tree(COMMITS_TREE, change_type_order)) - assert "Change types contain duplicated types" in str(excinfo) - def test_render_changelog( gitcommits, tags, changelog_content, any_changelog_format: ChangelogFormat @@ -1543,9 +1543,10 @@ def test_get_next_tag_name_after_version(tags): assert last_tag_name is None # Test error when version not found - with pytest.raises(changelog.NoCommitsFoundError) as exc_info: + with pytest.raises( + changelog.NoCommitsFoundError, match="Could not find a valid revision range" + ): changelog.get_next_tag_name_after_version(tags, "nonexistent") - assert "Could not find a valid revision range" in str(exc_info.value) @dataclass diff --git a/tests/test_cli.py b/tests/test_cli.py index c1f6d5bed..0fbc4876c 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -43,16 +43,14 @@ def test_invalid_command(util: UtilFixture, capsys, file_regression, arg): file_regression.check(err, extension=".txt") -def test_cz_config_file_without_correct_file_path(util: UtilFixture, capsys): - with pytest.raises(ConfigFileNotFound) as excinfo: +def test_cz_config_file_without_correct_file_path(util: UtilFixture): + with pytest.raises(ConfigFileNotFound, match="Cannot found the config file"): util.run_cli("--config", "./config/pyproject.toml", "example") - assert "Cannot found the config file" in str(excinfo.value) def test_cz_with_arg_but_without_command(util: UtilFixture): - with pytest.raises(NoCommandFoundError) as excinfo: + with pytest.raises(NoCommandFoundError, match="Command is required"): util.run_cli("--name", "cz_jira") - assert "Command is required" in str(excinfo.value) def test_name(util: UtilFixture, capsys): @@ -70,7 +68,7 @@ def test_name_default_value(util: UtilFixture, capsys): def test_ls(util: UtilFixture, capsys): util.run_cli("-n", "cz_jira", "ls") - out, err = capsys.readouterr() + out, _ = capsys.readouterr() assert "cz_conventional_commits" in out assert isinstance(out, str) @@ -85,7 +83,7 @@ def test_arg_debug(util: UtilFixture): assert excepthook.keywords.get("debug") is True -def test_commitizen_excepthook(capsys): +def test_commitizen_excepthook(): with pytest.raises(SystemExit) as excinfo: cli.commitizen_excepthook(NotAGitProjectError, NotAGitProjectError(), "") @@ -93,7 +91,7 @@ def test_commitizen_excepthook(capsys): assert excinfo.value.code == NotAGitProjectError.exit_code -def test_commitizen_debug_excepthook(capsys): +def test_commitizen_debug_excepthook(): with pytest.raises(SystemExit) as excinfo: cli.commitizen_excepthook( NotAGitProjectError, @@ -102,7 +100,6 @@ def test_commitizen_debug_excepthook(capsys): debug=True, ) - assert excinfo.type is SystemExit assert excinfo.value.code == NotAGitProjectError.exit_code assert "NotAGitProjectError" in str(excinfo.traceback[0]) @@ -119,12 +116,10 @@ def test_argcomplete_activation(): Equivalent to run: $ eval "$(register-python-argcomplete pytest)" """ - output = subprocess.run(["register-python-argcomplete", "cz"]) + subprocess.run(["register-python-argcomplete", "cz"], check=True) - assert output.returncode == 0 - -def test_commitizen_excepthook_no_raises(capsys): +def test_commitizen_excepthook_no_raises(): with pytest.raises(SystemExit) as excinfo: cli.commitizen_excepthook( NotAGitProjectError, @@ -165,17 +160,18 @@ def test_parse_no_raise(input_str, expected_result): def test_unknown_args_raises(util: UtilFixture): - with pytest.raises(InvalidCommandArgumentError) as excinfo: + with pytest.raises( + InvalidCommandArgumentError, match="Invalid commitizen arguments were found" + ): util.run_cli("c", "-this_arg_is_not_supported") - assert "Invalid commitizen arguments were found" in str(excinfo.value) def test_unknown_args_before_double_dash_raises(util: UtilFixture): - with pytest.raises(InvalidCommandArgumentError) as excinfo: + with pytest.raises( + InvalidCommandArgumentError, + match="Invalid commitizen arguments were found before -- separator", + ): util.run_cli("c", "-this_arg_is_not_supported", "--") - assert "Invalid commitizen arguments were found before -- separator" in str( - excinfo.value - ) def test_commitizen_excepthook_non_commitizen_exception(mocker: MockFixture): diff --git a/tests/test_conf.py b/tests/test_conf.py index c004e96e1..c01b96d38 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -2,6 +2,7 @@ import json import os +import re from pathlib import Path from typing import Any @@ -440,9 +441,8 @@ def test_init_with_invalid_config_content(self, tmp_path, config_file): path = tmp_path / "commitizen" / config_file path.parent.mkdir(parents=True, exist_ok=True) - with pytest.raises(InvalidConfigurationError) as excinfo: + with pytest.raises(InvalidConfigurationError, match=re.escape(config_file)): TomlConfig(data=existing_content, path=path) - assert config_file in str(excinfo.value) @pytest.mark.parametrize( @@ -467,9 +467,8 @@ def test_init_with_invalid_config_content(self, tmp_path, config_file): path = tmp_path / "commitizen" / config_file path.parent.mkdir(parents=True, exist_ok=True) - with pytest.raises(InvalidConfigurationError) as excinfo: + with pytest.raises(InvalidConfigurationError, match=re.escape(config_file)): JsonConfig(data=existing_content, path=path) - assert config_file in str(excinfo.value) @pytest.mark.parametrize( @@ -494,6 +493,5 @@ def test_init_with_invalid_content(self, tmp_path, config_file): path = tmp_path / "commitizen" / config_file path.parent.mkdir(parents=True, exist_ok=True) - with pytest.raises(InvalidConfigurationError) as excinfo: + with pytest.raises(InvalidConfigurationError, match=re.escape(config_file)): YAMLConfig(data=existing_content, path=path) - assert config_file in str(excinfo.value) diff --git a/tests/test_cz_customize.py b/tests/test_cz_customize.py index 311eea19a..fa058a639 100644 --- a/tests/test_cz_customize.py +++ b/tests/test_cz_customize.py @@ -1,3 +1,4 @@ +import re from pathlib import Path import pytest @@ -377,11 +378,12 @@ def config_with_unicode(request): def test_initialize_cz_customize_failed(): config = BaseConfig() - with pytest.raises(MissingCzCustomizeConfigError) as excinfo: + with pytest.raises( + MissingCzCustomizeConfigError, + match=re.escape(MissingCzCustomizeConfigError.message), + ): CustomizeCommitsCz(config) - assert MissingCzCustomizeConfigError.message in str(excinfo.value) - def test_bump_pattern(config): cz = CustomizeCommitsCz(config) diff --git a/tests/test_factory.py b/tests/test_factory.py index 303ae4e72..ea5868018 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -1,3 +1,4 @@ +import re import sys from importlib import metadata from textwrap import dedent @@ -31,11 +32,12 @@ def test_factory(): def test_factory_fails(): config = BaseConfig() config.settings.update({"name": "Nothing"}) - with pytest.raises(NoCommitizenFoundException) as excinfo: + with pytest.raises( + NoCommitizenFoundException, + match=re.escape("The committer has not been found in the system."), + ): factory.committer_factory(config) - assert "The committer has not been found in the system." in str(excinfo) - def test_discover_plugins(tmp_path): legacy_plugin_folder = tmp_path / "cz_legacy" diff --git a/tests/test_git.py b/tests/test_git.py index 51586eb22..db0ce4039 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -413,9 +413,8 @@ def test_get_filenames_in_commit_with_git_reference(util: UtilFixture): def test_get_filenames_in_commit_error(util: UtilFixture): """Test that GitCommandError is raised when git command fails.""" util.mock_cmd(err="fatal: bad object HEAD", return_code=1) - with pytest.raises(GitCommandError) as excinfo: + with pytest.raises(GitCommandError, match="fatal: bad object HEAD"): git.get_filenames_in_commit() - assert str(excinfo.value) == "fatal: bad object HEAD" @pytest.mark.parametrize(