From 37d09f74260b43aa92f01314a49fa58fc74f76ce 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 | 65 +++++++++++----------- 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 | 29 +++++----- tests/test_changelog.py | 11 ++-- tests/test_cli.py | 32 +++++------ tests/test_conf.py | 10 ++-- tests/test_cz_customize.py | 6 +- tests/test_factory.py | 7 ++- tests/test_git.py | 3 +- 11 files changed, 103 insertions(+), 120 deletions(-) diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index 6b8d903d68..cb4161798e 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -303,9 +303,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") @@ -327,18 +326,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): @@ -349,11 +347,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 @@ -369,11 +367,11 @@ 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="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 @@ -394,13 +392,12 @@ 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="--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.join("__version__.py") @@ -538,13 +535,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 +733,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 +768,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 c15ffeee9c..2f6fbcf328 100644 --- a/tests/commands/test_changelog_command.py +++ b/tests/commands/test_changelog_command.py @@ -877,11 +877,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 f225e912ea..539b32755c 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): "commitizen.commands.check.open", mocker.mock_open(read_data="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( @@ -169,30 +168,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): @@ -206,9 +206,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): @@ -279,9 +281,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( @@ -450,11 +451,10 @@ def test_check_command_with_custom_validator_failed( read_data="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 b3b3791153..02fbfa4170 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -1,3 +1,4 @@ +import re from pathlib import Path from unittest.mock import ANY @@ -81,11 +82,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( @@ -206,11 +207,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): @@ -239,12 +238,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 1a1afade0c..927b0f9deb 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 @@ -193,7 +194,12 @@ 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}." + ), + ): bump.update_version_in_files( old_version, new_version, @@ -202,13 +208,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 @@ -277,7 +276,12 @@ 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}" + ), + ): bump.update_version_in_files( old_version, new_version, @@ -286,13 +290,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 60dae8b50d..641b85760d 100644 --- a/tests/test_changelog.py +++ b/tests/test_changelog.py @@ -1229,11 +1229,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 @@ -1544,9 +1544,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 f504a8cb6c..fb12e61f39 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -44,15 +44,13 @@ def test_invalid_command(util: UtilFixture, capsys, file_regression, arg): def test_cz_config_file_without_correct_file_path(util: UtilFixture, capsys): - with pytest.raises(ConfigFileNotFound) as excinfo: + 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 923535e0c8..e89b689443 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 @@ -409,9 +410,8 @@ def test_init_with_invalid_config_content(self, tmpdir, config_file): existing_content = "invalid toml content" path = tmpdir.mkdir("commitizen").join(config_file) - 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( @@ -434,9 +434,8 @@ def test_init_with_invalid_config_content(self, tmpdir, config_file): existing_content = "invalid json content" path = tmpdir.mkdir("commitizen").join(config_file) - 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( @@ -459,6 +458,5 @@ def test_init_with_invalid_content(self, tmpdir, config_file): existing_content = "invalid: .cz.yaml: content: maybe?" path = tmpdir.mkdir("commitizen").join(config_file) - 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 22a3e80f38..277bbf0495 100644 --- a/tests/test_cz_customize.py +++ b/tests/test_cz_customize.py @@ -377,11 +377,11 @@ def config_with_unicode(request): def test_initialize_cz_customize_failed(): config = BaseConfig() - with pytest.raises(MissingCzCustomizeConfigError) as excinfo: + with pytest.raises( + MissingCzCustomizeConfigError, match=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 303ae4e728..23848efc65 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -31,11 +31,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="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 9333997813..b41c17f2c6 100644 --- a/tests/test_git.py +++ b/tests/test_git.py @@ -390,9 +390,8 @@ def test_commit_with_spaces_in_path( 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(