tests: switch to mock.patch.object

Python style reviewers often point people to use mock.patch.object
since it can be statically checked better.  Migrate code, and update
the SKILL file to cover this too.

Change-Id: If7aa8e3ddcdb3d739187b12e3034de1de990a7df
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/656246
Reviewed-by: Brian Gan <brgan@google.com>
Tested-by: Mike Frysinger <vapier@google.com>
Commit-Queue: Mike Frysinger <vapier@google.com>
diff --git a/agents/skills/core_internals/SKILL.md b/agents/skills/core_internals/SKILL.md
index a764ad3..2835ba4 100644
--- a/agents/skills/core_internals/SKILL.md
+++ b/agents/skills/core_internals/SKILL.md
@@ -3471,6 +3471,12 @@
 :           :                                 :          : asserted directly   :
 :           :                                 :          : in the test         :
 :           :                                 :          : docstring.          :
+| **T5-29** | Object-Referenced Mocking via   | High     | Passing a dotted    |
+:           : `mock.patch.object`             :          : string target to    :
+:           :                                 :          : `mock.patch`        :
+:           :                                 :          : instead of using    :
+:           :                                 :          : `mock.patch.object` :
+:           :                                 :          : on the object.      :
 
 --------------------------------------------------------------------------------
 
@@ -3903,13 +3909,13 @@
 **Don't:**
 
 ```python
-@mock.patch("main.time.sleep")
+@mock.patch.object(main.time, "sleep")
 ```
 
 **Do:**
 
 ```python
-@mock.patch("time.sleep")
+@mock.patch.object(time, "sleep")
 ```
 
 --------------------------------------------------------------------------------
@@ -3939,7 +3945,7 @@
 ```python
 class SshTests(unittest.TestCase):
     def test_version(self):
-        with mock.patch("ssh._run_ssh_version", return_value="OpenSSH_1.2\n"):
+        with mock.patch.object(ssh, "_run_ssh_version", return_value="OpenSSH_1.2\n"):
             self.assertEqual(ssh.version(), (1, 2))
 ```
 
@@ -3951,7 +3957,7 @@
         ssh.version.cache_clear()
 
     def test_version(self):
-        with mock.patch("ssh._run_ssh_version", return_value="OpenSSH_1.2\n"):
+        with mock.patch.object(ssh, "_run_ssh_version", return_value="OpenSSH_1.2\n"):
             self.assertEqual(ssh.version(), (1, 2))
 ```
 
@@ -4015,15 +4021,15 @@
 **Don't:**
 
 ```python
-with mock.patch("sys.stderr") as mock_stderr:
-    with mock.patch("socket.socket") as ms:
+with mock.patch.object(time, "sleep") as mock_sleep:
+    with mock.patch.object(socket, "socket") as ms:
         run_test()
 ```
 
 **Do:**
 
 ```python
-with mock.patch("sys.stderr") as mock_stderr, mock.patch("socket.socket") as ms:
+with mock.patch.object(time, "sleep") as mock_sleep, mock.patch.object(socket, "socket") as ms:
     run_test()
 ```
 
@@ -4075,7 +4081,7 @@
 **Do:**
 
 ```python
-mock.patch("socket.socket", side_effect=OSError)
+mock.patch.object(socket, "socket", side_effect=OSError)
 ```
 
 **Exceptions:** Tests explicitly verifying exact formatting serialization (e.g.,
@@ -4613,6 +4619,43 @@
 
 --------------------------------------------------------------------------------
 
+#### T5-29: Object-Referenced Mocking via `mock.patch.object`
+
+> **Rule:** Never use `mock.patch`; always use `mock.patch.object`.
+>
+> **What:** All attribute and function patching in unit tests must use
+> `mock.patch.object(target, "attribute", ...)` against an explicitly imported
+> module or class object rather than passing dotted string paths to
+> `mock.patch("module.attribute", ...)`.
+>
+> **Applies To:** All unit tests utilizing `unittest.mock`.
+>
+> **Why:** String-based `mock.patch` paths bypass static analysis and module
+> import validation, making tests brittle during refactoring and prone to silent
+> resolution errors or import ordering bugs. Referencing the imported object
+> directly via `mock.patch.object` ensures the target module is explicitly
+> imported and validated at load time. Failing to adhere to this typically
+> results in **Brittle Patch Routing / Unvalidated String Targets**.
+
+**Trap 1: Passing a dotted string target to `mock.patch` instead of using
+`mock.patch.object` on the object.**
+
+**Don't:**
+
+```python
+with mock.patch("ssh._run_ssh_version", return_value="OpenSSH_1.2\n"):
+    ...
+```
+
+**Do:**
+
+```python
+with mock.patch.object(ssh, "_run_ssh_version", return_value="OpenSSH_1.2\n"):
+    ...
+```
+
+--------------------------------------------------------------------------------
+
 ### Cross-Domain Dependencies
 
 *   **Upstream:** T2 | Filesystem Atomicity & Worktree Layout - *Managing safe
diff --git a/tests/test_color.py b/tests/test_color.py
index 72d7499..57a9a13 100644
--- a/tests/test_color.py
+++ b/tests/test_color.py
@@ -14,6 +14,7 @@
 
 """Unittests for the color.py module."""
 
+import os
 from unittest import mock
 
 import pytest
@@ -21,6 +22,7 @@
 
 import color
 import git_config
+import pager
 
 
 @pytest.fixture
@@ -147,8 +149,8 @@
         pager_active: bool,
         expected: bool,
     ) -> None:
-        with mock.patch("os.isatty", return_value=isatty), mock.patch(
-            "pager.active", pager_active
-        ):
+        with mock.patch.object(
+            os, "isatty", return_value=isatty
+        ), mock.patch.object(pager, "active", pager_active):
             c = _make_coloring(state)
         assert c.is_on is expected
diff --git a/tests/test_git_command.py b/tests/test_git_command.py
index dccc831..903322f 100644
--- a/tests/test_git_command.py
+++ b/tests/test_git_command.py
@@ -18,6 +18,7 @@
 import os
 import re
 import subprocess
+import sys
 import unittest
 from unittest import mock
 
@@ -76,7 +77,7 @@
 
         self.mock_popen = mock.MagicMock()
         self.mock_popen.return_value = self.mock_process
-        mock.patch("subprocess.Popen", self.mock_popen).start()
+        mock.patch.object(subprocess, "Popen", self.mock_popen).start()
 
     def tearDown(self):
         mock.patch.stopall()
@@ -145,7 +146,7 @@
         self.mock_process.communicate.assert_called_once_with(input=None)
         self.mock_process.stderr.read1.assert_not_called()
 
-    @mock.patch("sys.stderr")
+    @mock.patch.object(sys, "stderr")
     def test_streams_stderr_when_no_stream_is_set(self, mock_stderr):
         mock_stderr.isatty.return_value = False
         logs = "\n".join(
@@ -175,7 +176,7 @@
         mock_stderr.write.assert_called_once_with(logs)
         self.assertEqual(cmd.stderr, logs)
 
-    @mock.patch("sys.stderr")
+    @mock.patch.object(sys, "stderr")
     def test_push_injects_progress_only_when_tty_and_not_suppressed(
         self, mock_stderr: mock.MagicMock
     ) -> None:
@@ -186,7 +187,7 @@
         cmd = self.mock_popen.call_args[0][0]
         self.assertEqual(cmd, ["git", "push", "--progress"])
 
-    @mock.patch("sys.stderr")
+    @mock.patch.object(sys, "stderr")
     def test_push_does_not_inject_progress_when_no_progress_set(
         self, mock_stderr: mock.MagicMock
     ) -> None:
@@ -209,7 +210,7 @@
 
         self.mock_popen = mock.MagicMock()
         self.mock_popen.return_value = self.mock_process
-        mock.patch("subprocess.Popen", self.mock_popen).start()
+        mock.patch.object(subprocess, "Popen", self.mock_popen).start()
 
     def tearDown(self) -> None:
         mock.patch.stopall()
diff --git a/tests/test_git_superproject.py b/tests/test_git_superproject.py
index e7f208d..ba68a91 100644
--- a/tests/test_git_superproject.py
+++ b/tests/test_git_superproject.py
@@ -24,6 +24,7 @@
 import pytest
 import test_manifest_xml
 
+import git_command
 import git_superproject
 import git_trace2_event_log
 import manifest_xml
@@ -464,8 +465,8 @@
     def test_Init_success(self):
         """Test _Init succeeds and creates the work git dir."""
         self.assertFalse(os.path.exists(self._superproject._work_git))
-        with mock.patch(
-            "git_command.GitCommand", autospec=True
+        with mock.patch.object(
+            git_command, "GitCommand", autospec=True
         ) as mock_git_command:
             instance = mock_git_command.return_value
             instance.Wait.return_value = 0
@@ -491,16 +492,16 @@
         """Test _Init returns early if the work git dir already exists."""
         os.mkdir(self._superproject._superproject_path)
         os.mkdir(self._superproject._work_git)
-        with mock.patch(
-            "git_command.GitCommand", autospec=True
+        with mock.patch.object(
+            git_command, "GitCommand", autospec=True
         ) as mock_git_command:
             self.assertTrue(self._superproject._Init())
             mock_git_command.assert_not_called()
 
     def test_Init_failure_cleans_up(self):
         """Test _Init cleans up the temporary directory if 'git init' fails."""
-        with mock.patch(
-            "git_command.GitCommand", autospec=True
+        with mock.patch.object(
+            git_command, "GitCommand", autospec=True
         ) as mock_git_command:
             instance = mock_git_command.return_value
             instance.Wait.return_value = 1
@@ -539,8 +540,8 @@
         os.mkdir(self._superproject._superproject_path)
         os.mkdir(self._superproject._work_git)
         with mock.patch.object(self._superproject, "_Init", return_value=True):
-            with mock.patch(
-                "git_command.GitCommand", autospec=True
+            with mock.patch.object(
+                git_command, "GitCommand", autospec=True
             ) as mock_git_command:
                 with mock.patch.object(
                     self._superproject, "_GetRef"
@@ -599,12 +600,12 @@
     def test_GetRef_resolves_only_the_requested_ref(self) -> None:
         command = mock.MagicMock(stdout="1234\n")
         command.Wait.return_value = 0
-        with mock.patch(
-            "git_command.GitCommand", return_value=command
-        ) as git_command:
+        with mock.patch.object(
+            git_command, "GitCommand", return_value=command
+        ) as mock_git_command:
             self.assertEqual("1234", self._superproject._GetRef("HEAD"))
 
-        git_command.assert_called_once_with(
+        mock_git_command.assert_called_once_with(
             None,
             ["rev-parse", "--verify", "--quiet", "HEAD"],
             gitdir=self._superproject._work_git,
diff --git a/tests/test_git_trace2_event_log.py b/tests/test_git_trace2_event_log.py
index 195dbaf..7b8b155 100644
--- a/tests/test_git_trace2_event_log.py
+++ b/tests/test_git_trace2_event_log.py
@@ -437,7 +437,9 @@
         event_log.verbose = False
         with contextlib.redirect_stderr(
             io.StringIO()
-        ) as mock_stderr, mock.patch("socket.socket", side_effect=OSError):
+        ) as mock_stderr, mock.patch.object(
+            socket, "socket", side_effect=OSError
+        ):
             event_log.Write(path="af_unix:stream:/tmp/test_sock")
             assert mock_stderr.getvalue() == ""
 
@@ -447,8 +449,8 @@
         event_log.verbose = True
         with contextlib.redirect_stderr(
             io.StringIO()
-        ) as mock_stderr, mock.patch(
-            "socket.socket", side_effect=OSError("Mock error")
+        ) as mock_stderr, mock.patch.object(
+            socket, "socket", side_effect=OSError("Mock error")
         ):
             event_log.Write(path="af_unix:stream:/tmp/test_sock")
             assert (
@@ -462,8 +464,8 @@
         event_log.verbose = False
         with contextlib.redirect_stderr(
             io.StringIO()
-        ) as mock_stderr, mock.patch(
-            "tempfile.NamedTemporaryFile", side_effect=FileExistsError
+        ) as mock_stderr, mock.patch.object(
+            tempfile, "NamedTemporaryFile", side_effect=FileExistsError
         ):
             event_log.Write(path="/tmp")
             assert mock_stderr.getvalue() == ""
@@ -474,8 +476,9 @@
         event_log.verbose = True
         with contextlib.redirect_stderr(
             io.StringIO()
-        ) as mock_stderr, mock.patch(
-            "tempfile.NamedTemporaryFile",
+        ) as mock_stderr, mock.patch.object(
+            tempfile,
+            "NamedTemporaryFile",
             side_effect=FileExistsError("Mock error"),
         ):
             event_log.Write(path="/tmp")
diff --git a/tests/test_main.py b/tests/test_main.py
index 74c6d9a..2545caa 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -14,13 +14,16 @@
 
 """Tests for the main repo script and subcommand routing."""
 
+import builtins
 from pathlib import Path
+import time
 from unittest import mock
 
 import pytest
 
 import event_log
 import main
+import manifest_xml
 
 
 @pytest.fixture(name="repo")
@@ -38,7 +41,7 @@
     return mock.MagicMock()
 
 
-@mock.patch("time.sleep")
+@mock.patch.object(time, "sleep")
 def test_autocorrect_delay(mock_sleep, repo, mock_config):
     """Test autocorrect with positive delay."""
     mock_config.GetString.return_value = "10"
@@ -50,7 +53,7 @@
     assert res == "start"
 
 
-@mock.patch("time.sleep")
+@mock.patch.object(time, "sleep")
 def test_autocorrect_delay_one(mock_sleep, repo, mock_config):
     """Test autocorrect with '1' (0.1s delay, not immediate)."""
     mock_config.GetString.return_value = "1"
@@ -61,7 +64,7 @@
     assert res == "start"
 
 
-@mock.patch("time.sleep", side_effect=KeyboardInterrupt())
+@mock.patch.object(time, "sleep", side_effect=KeyboardInterrupt())
 def test_autocorrect_delay_interrupt(mock_sleep, repo, mock_config):
     """Test autocorrect handles KeyboardInterrupt during delay."""
     mock_config.GetString.return_value = "10"
@@ -72,7 +75,7 @@
     assert res is None
 
 
-@mock.patch("time.sleep")
+@mock.patch.object(time, "sleep")
 def test_autocorrect_immediate(mock_sleep, repo, mock_config):
     """Test autocorrect with immediate/negative delay."""
     # Test numeric negative.
@@ -125,7 +128,7 @@
     assert res is None
 
 
-@mock.patch("builtins.input", return_value="y")
+@mock.patch.object(builtins, "input", return_value="y")
 def test_autocorrect_prompt_yes(mock_input, repo, mock_config):
     """Test autocorrect with prompt and user answers yes."""
     mock_config.GetString.return_value = "prompt"
@@ -135,7 +138,7 @@
     assert res == "start"
 
 
-@mock.patch("builtins.input", return_value="n")
+@mock.patch.object(builtins, "input", return_value="n")
 def test_autocorrect_prompt_no(mock_input, repo, mock_config):
     """Test autocorrect with prompt and user answers no."""
     mock_config.GetString.return_value = "prompt"
@@ -145,7 +148,7 @@
     assert res is None
 
 
-@mock.patch("builtins.input", return_value="y")
+@mock.patch.object(builtins, "input", return_value="y")
 def test_autocorrect_multiple_candidates(mock_input, repo, mock_config):
     """Test autocorrect with multiple matches forces a prompt."""
     mock_config.GetString.return_value = "10"  # Normally just delay
@@ -158,7 +161,7 @@
     assert res == "start"
 
 
-@mock.patch("builtins.input", side_effect=KeyboardInterrupt())
+@mock.patch.object(builtins, "input", side_effect=KeyboardInterrupt())
 def test_autocorrect_prompt_interrupt(mock_input, repo, mock_config):
     """Test autocorrect with prompt and user interrupts."""
     mock_config.GetString.return_value = "prompt"
@@ -181,7 +184,7 @@
     project.GetCommitRevisionId.assert_not_called()
 
 
-@mock.patch("manifest_xml.RepoClient")
+@mock.patch.object(manifest_xml, "RepoClient")
 def test_run_long_gates_event_log_on_flag(
     mock_client_cls: mock.MagicMock, tmp_path: Path
 ) -> None:
diff --git a/tests/test_project.py b/tests/test_project.py
index 9b952e4..89fa1a5 100644
--- a/tests/test_project.py
+++ b/tests/test_project.py
@@ -37,6 +37,7 @@
 import manifest_xml
 import platform_utils
 import project
+import repo_trace
 
 
 class FakeProject:
@@ -274,8 +275,10 @@
         """Check UploadForReview emits repo.uploadstate trace2 data events."""
         proj, _ = self._create_project_for_upload_test()
 
-        with mock.patch("git_command.GitCommand") as mock_git_cmd, mock.patch(
-            "project.ReviewableBranch"
+        with mock.patch.object(
+            git_command, "GitCommand"
+        ) as mock_git_cmd, mock.patch.object(
+            project, "ReviewableBranch"
         ) as mock_rb_cls:
             mock_cmd = mock.MagicMock()
             mock_cmd.Wait.return_value = 0
@@ -314,8 +317,10 @@
         """Check event emission when stderr contains no review URLs."""
         proj, _ = self._create_project_for_upload_test()
 
-        with mock.patch("git_command.GitCommand") as mock_git_cmd, mock.patch(
-            "project.ReviewableBranch"
+        with mock.patch.object(
+            git_command, "GitCommand"
+        ) as mock_git_cmd, mock.patch.object(
+            project, "ReviewableBranch"
         ) as mock_rb_cls:
             mock_cmd = mock.MagicMock()
             mock_cmd.Wait.return_value = 0
@@ -348,8 +353,10 @@
         """Check UploadForReview succeeds when git_event_log is None."""
         proj, _ = self._create_project_for_upload_test()
 
-        with mock.patch("git_command.GitCommand") as mock_git_cmd, mock.patch(
-            "project.ReviewableBranch"
+        with mock.patch.object(
+            git_command, "GitCommand"
+        ) as mock_git_cmd, mock.patch.object(
+            project, "ReviewableBranch"
         ) as mock_rb_cls:
             mock_cmd = mock.MagicMock()
             mock_cmd.Wait.return_value = 0
@@ -371,9 +378,13 @@
         """Check exceptions during tracing are caught and do not fail upload."""
         proj, _ = self._create_project_for_upload_test()
 
-        with mock.patch("git_command.GitCommand") as mock_git_cmd, mock.patch(
-            "project.ReviewableBranch"
-        ) as mock_rb_cls, mock.patch("project.logger") as mock_logger:
+        with mock.patch.object(
+            git_command, "GitCommand"
+        ) as mock_git_cmd, mock.patch.object(
+            project, "ReviewableBranch"
+        ) as mock_rb_cls, mock.patch.object(
+            project, "logger"
+        ) as mock_logger:
             mock_cmd = mock.MagicMock()
             mock_cmd.Wait.return_value = 0
             mock_cmd.stderr = "remote:   https://example.com/c/test/+/1\n"
@@ -396,8 +407,10 @@
         """Check integration with real git_trace2_event_log.EventLog."""
         proj, _ = self._create_project_for_upload_test()
 
-        with mock.patch("git_command.GitCommand") as mock_git_cmd, mock.patch(
-            "project.ReviewableBranch"
+        with mock.patch.object(
+            git_command, "GitCommand"
+        ) as mock_git_cmd, mock.patch.object(
+            project, "ReviewableBranch"
         ) as mock_rb_cls:
             mock_cmd = mock.MagicMock()
             mock_cmd.Wait.return_value = 0
@@ -439,9 +452,11 @@
         for quiet in (False, True):
             with self.subTest(quiet=quiet):
                 proj, _ = self._create_project_for_upload_test()
-                with mock.patch(
-                    "git_command.GitCommand"
-                ) as mock_git_cmd, mock.patch("project.ReviewableBranch"):
+                with mock.patch.object(
+                    git_command, "GitCommand"
+                ) as mock_git_cmd, mock.patch.object(
+                    project, "ReviewableBranch"
+                ):
                     mock_cmd = mock.MagicMock()
                     mock_cmd.Wait.return_value = 0
                     mock_cmd.stderr = ""
@@ -1195,7 +1210,7 @@
             with open(head_file, "w", encoding="utf-8", newline="") as fp:
                 fp.write("ref:\t  refs/heads/fallback-branch\r\n")
 
-            with mock.patch("platform_utils.islink", return_value=True):
+            with mock.patch.object(platform_utils, "islink", return_value=True):
                 with mock.patch.object(
                     fakeproj.work_git,
                     "symbolic_ref",
@@ -1215,7 +1230,7 @@
             with open(head_file, "w", encoding="utf-8", newline="") as fp:
                 fp.write(f"{sha_upper}\r\n")
 
-            with mock.patch("platform_utils.islink", return_value=True):
+            with mock.patch.object(platform_utils, "islink", return_value=True):
                 with mock.patch.object(
                     fakeproj.work_git,
                     "symbolic_ref",
@@ -1233,7 +1248,7 @@
             with open(head_file, "w", encoding="utf-8", newline="") as fp:
                 fp.write("ref: refs/heads/.invalid\r\n")
 
-            with mock.patch("platform_utils.islink", return_value=True):
+            with mock.patch.object(platform_utils, "islink", return_value=True):
                 with mock.patch.object(
                     fakeproj.work_git,
                     "symbolic_ref",
@@ -1476,7 +1491,7 @@
             hex41 = "a" * 41
             proj.SetRevision(hex41)
 
-            with mock.patch("git_command.GitCommand") as mock_git:
+            with mock.patch.object(git_command, "GitCommand") as mock_git:
                 mock_cmd = mock.MagicMock()
                 mock_cmd.Wait.return_value = 0
                 mock_git.return_value = mock_cmd
@@ -2490,7 +2505,7 @@
             proj.work_git = mock.MagicMock()
             syncbuf = project.SyncBuffer(proj.config)
 
-            with mock.patch("git_command.GitCommand") as mock_git_cmd:
+            with mock.patch.object(git_command, "GitCommand") as mock_git_cmd:
                 mock_cmd_instance = mock.MagicMock()
                 mock_cmd_instance.Wait.return_value = 0
                 mock_git_cmd.return_value = mock_cmd_instance
@@ -2839,7 +2854,7 @@
             if os.path.exists(shallow_path):
                 os.unlink(shallow_path)
 
-            with mock.patch("git_command.GitCommand") as mock_git_cmd:
+            with mock.patch.object(git_command, "GitCommand") as mock_git_cmd:
                 mock_cmd_instance = mock.MagicMock()
                 mock_cmd_instance.Wait.return_value = 0
                 mock_git_cmd.return_value = mock_cmd_instance
@@ -2862,7 +2877,7 @@
             with open(os.path.join(proj.gitdir, "shallow"), "w") as f:
                 f.write("")
 
-            with mock.patch("git_command.GitCommand") as mock_git_cmd:
+            with mock.patch.object(git_command, "GitCommand") as mock_git_cmd:
                 res = proj._RemoteFetch(
                     current_branch_only=True,
                     depth=1,
@@ -2885,7 +2900,7 @@
             with open(os.path.join(other.gitdir, "shallow"), "w") as f:
                 f.write("")
 
-            with mock.patch("git_command.GitCommand") as mock_git_cmd:
+            with mock.patch.object(git_command, "GitCommand") as mock_git_cmd:
                 mock_cmd_instance = mock.MagicMock()
                 mock_cmd_instance.Wait.return_value = 0
                 mock_git_cmd.return_value = mock_cmd_instance
@@ -2914,7 +2929,7 @@
             with open(os.path.join(other.gitdir, "shallow"), "w") as f:
                 f.write("")
 
-            with mock.patch("git_command.GitCommand") as mock_git_cmd:
+            with mock.patch.object(git_command, "GitCommand") as mock_git_cmd:
                 res = proj._RemoteFetch(
                     current_branch_only=True,
                     depth=1,
@@ -3108,7 +3123,7 @@
             mock_remote.PreConnectFetch.return_value = True
             proj.GetRemote = mock.MagicMock(return_value=mock_remote)
 
-            with mock.patch("git_command.GitCommand") as mock_git_cmd:
+            with mock.patch.object(git_command, "GitCommand") as mock_git_cmd:
                 mock_cmd_instance = mock.MagicMock()
                 mock_cmd_instance.Wait.return_value = 0
                 mock_git_cmd.return_value = mock_cmd_instance
@@ -3147,7 +3162,7 @@
             mock_remote.PreConnectFetch.return_value = True
             proj.GetRemote = mock.MagicMock(return_value=mock_remote)
 
-            with mock.patch("git_command.GitCommand") as mock_git_cmd:
+            with mock.patch.object(git_command, "GitCommand") as mock_git_cmd:
                 mock_cmd_instance = mock.MagicMock()
                 mock_cmd_instance.Wait.return_value = 0
                 mock_git_cmd.return_value = mock_cmd_instance
@@ -3189,7 +3204,7 @@
             mock_remote.PreConnectFetch.return_value = True
             proj.GetRemote = mock.MagicMock(return_value=mock_remote)
 
-            with mock.patch("git_command.GitCommand") as mock_git_cmd:
+            with mock.patch.object(git_command, "GitCommand") as mock_git_cmd:
                 mock_cmd_instance = mock.MagicMock()
                 mock_cmd_instance.Wait.return_value = 0
                 mock_git_cmd.return_value = mock_cmd_instance
@@ -3237,7 +3252,7 @@
             mock_remote.PreConnectFetch.return_value = True
             proj.GetRemote = mock.MagicMock(return_value=mock_remote)
 
-            with mock.patch("git_command.GitCommand") as mock_git_cmd:
+            with mock.patch.object(git_command, "GitCommand") as mock_git_cmd:
                 mock_cmd_instance = mock.MagicMock()
                 mock_cmd_instance.Wait.return_value = 0
                 mock_git_cmd.return_value = mock_cmd_instance
@@ -3338,8 +3353,8 @@
                     inst.stdout = ""
                 return inst
 
-            with mock.patch(
-                "git_command.GitCommand", side_effect=_fake_git_command
+            with mock.patch.object(
+                git_command, "GitCommand", side_effect=_fake_git_command
             ):
                 res = proj._RemoteFetch(current_branch_only=True)
 
@@ -3490,7 +3505,7 @@
             mock_remote.ToLocal.return_value = "refs/remotes/origin/main"
             proj.GetRemote = mock.MagicMock(return_value=mock_remote)
 
-            with mock.patch("subprocess.run") as mock_run:
+            with mock.patch.object(subprocess, "run") as mock_run:
                 mock_run.return_value = mock.MagicMock(returncode=0, stderr="")
                 res = proj._CustomFetch()
 
@@ -3523,7 +3538,7 @@
             mock_remote.ToLocal.return_value = "refs/remotes/origin/main"
             proj.GetRemote = mock.MagicMock(return_value=mock_remote)
 
-            with mock.patch("subprocess.run") as mock_run:
+            with mock.patch.object(subprocess, "run") as mock_run:
                 mock_run.return_value = mock.MagicMock(returncode=0, stderr="")
                 res = proj._CustomFetch()
 
@@ -3543,7 +3558,7 @@
             proj.bare_git.ResolveRef.return_value = sha
             proj.GetRemote = mock.MagicMock()
 
-            with mock.patch("subprocess.run") as mock_run:
+            with mock.patch.object(subprocess, "run") as mock_run:
                 mock_run.return_value = mock.MagicMock(returncode=0, stderr="")
                 res = proj._CustomFetch()
 
@@ -3571,7 +3586,7 @@
             mock_remote.ToLocal.return_value = "refs/remotes/origin/main"
             proj.GetRemote = mock.MagicMock(return_value=mock_remote)
 
-            with mock.patch("subprocess.run") as mock_run:
+            with mock.patch.object(subprocess, "run") as mock_run:
                 mock_run.return_value = mock.MagicMock(returncode=0, stderr="")
                 self.assertFalse(proj._CustomFetch())
 
@@ -3588,7 +3603,7 @@
             mock_remote.ToLocal.return_value = "refs/remotes/origin/main"
             proj.GetRemote = mock.MagicMock(return_value=mock_remote)
 
-            with mock.patch("subprocess.run") as mock_run:
+            with mock.patch.object(subprocess, "run") as mock_run:
                 mock_run.return_value = mock.MagicMock(returncode=0, stderr="")
                 self.assertFalse(proj._CustomFetch())
 
@@ -3603,7 +3618,7 @@
             mock_remote.ToLocal.return_value = "refs/remotes/origin/main"
             proj.GetRemote = mock.MagicMock(return_value=mock_remote)
 
-            with mock.patch("subprocess.run") as mock_run:
+            with mock.patch.object(subprocess, "run") as mock_run:
                 mock_run.return_value = mock.MagicMock(returncode=0, stderr="")
                 self.assertFalse(proj._CustomFetch())
 
@@ -3618,7 +3633,7 @@
             proj.revisionExpr = sha
             proj.bare_git.ResolveRef.side_effect = error.GitError("missing")
 
-            with mock.patch("subprocess.run") as mock_run:
+            with mock.patch.object(subprocess, "run") as mock_run:
                 mock_run.return_value = mock.MagicMock(returncode=0, stderr="")
                 self.assertFalse(proj._CustomFetch())
 
@@ -3639,7 +3654,7 @@
             mock_remote.ToLocal.return_value = "refs/tags/v1.0"
             proj.GetRemote = mock.MagicMock(return_value=mock_remote)
 
-            with mock.patch("subprocess.run") as mock_run:
+            with mock.patch.object(subprocess, "run") as mock_run:
                 mock_run.return_value = mock.MagicMock(returncode=0, stderr="")
                 self.assertTrue(proj._CustomFetch())
 
@@ -3667,7 +3682,7 @@
             mock_remote.ToLocal.return_value = "refs/tags/v1.0"
             proj.GetRemote = mock.MagicMock(return_value=mock_remote)
 
-            with mock.patch("subprocess.run") as mock_run:
+            with mock.patch.object(subprocess, "run") as mock_run:
                 mock_run.return_value = mock.MagicMock(returncode=0, stderr="")
                 self.assertFalse(proj._CustomFetch())
 
@@ -3707,7 +3722,7 @@
                     )
                 return mock.MagicMock(returncode=0, stderr="")
 
-            with mock.patch("subprocess.run", side_effect=_edit_config):
+            with mock.patch.object(subprocess, "run", side_effect=_edit_config):
                 self.assertTrue(proj._CustomFetch())
 
             self.assertTrue(proj.config.GetBoolean("custom.initialized"))
@@ -3730,7 +3745,9 @@
                     fp.write("[custom]\n\tfailedrun = true\n")
                 return mock.MagicMock(returncode=2, stderr="boom")
 
-            with mock.patch("subprocess.run", side_effect=_fail_after_edit):
+            with mock.patch.object(
+                subprocess, "run", side_effect=_fail_after_edit
+            ):
                 self.assertFalse(proj._CustomFetch())
             self.assertTrue(proj.config.GetBoolean("custom.failedrun"))
 
@@ -3790,9 +3807,9 @@
             mock_remote.ToLocal.return_value = "refs/remotes/origin/main"
             proj.GetRemote = mock.MagicMock(return_value=mock_remote)
 
-            with mock.patch("repo_trace.Trace") as mock_trace, mock.patch(
-                "subprocess.run"
-            ) as mock_run:
+            with mock.patch.object(
+                repo_trace, "Trace"
+            ) as mock_trace, mock.patch.object(subprocess, "run") as mock_run:
                 mock_run.return_value = mock.MagicMock(returncode=0, stderr="")
                 self.assertTrue(proj._CustomFetch())
 
@@ -3805,9 +3822,9 @@
             )
 
             proj.revisionId = sha
-            with mock.patch("repo_trace.Trace") as mock_trace, mock.patch(
-                "subprocess.run"
-            ) as mock_run:
+            with mock.patch.object(
+                repo_trace, "Trace"
+            ) as mock_trace, mock.patch.object(subprocess, "run") as mock_run:
                 mock_run.return_value = mock.MagicMock(returncode=0, stderr="")
                 self.assertTrue(proj._CustomFetch())
 
@@ -3819,9 +3836,9 @@
                 sha,
             )
 
-            with mock.patch(
-                "repo_trace.Trace", side_effect=OSError("trace disk full")
-            ), mock.patch("subprocess.run") as mock_run:
+            with mock.patch.object(
+                repo_trace, "Trace", side_effect=OSError("trace disk full")
+            ), mock.patch.object(subprocess, "run") as mock_run:
                 with self.assertRaises(OSError):
                     proj._CustomFetch()
                 mock_run.assert_not_called()
@@ -4039,9 +4056,9 @@
         self, proj: project.Project, **outputs: Any
     ) -> mock.MagicMock:
         """Run _Reproject against mocked Git; return the subprocess mock."""
-        with mock.patch(
-            "git_command.GitCommand", side_effect=self._git_command(**outputs)
-        ), mock.patch("subprocess.run") as mock_run:
+        with mock.patch.object(
+            git_command, "GitCommand", side_effect=self._git_command(**outputs)
+        ), mock.patch.object(subprocess, "run") as mock_run:
             mock_run.return_value = mock.MagicMock(returncode=0, stdout="")
             proj._Reproject(self.REVID)
         return mock_run
@@ -4050,9 +4067,9 @@
         """Test the command runs from the client root with REPO_TREV set."""
         with utils_for_test.TempGitTree() as tempdir:
             proj = self._get_project(tempdir)
-            with mock.patch(
-                "git_command.GitCommand", side_effect=self._git_command()
-            ), mock.patch("subprocess.run") as mock_run:
+            with mock.patch.object(
+                git_command, "GitCommand", side_effect=self._git_command()
+            ), mock.patch.object(subprocess, "run") as mock_run:
                 mock_run.return_value = mock.MagicMock(returncode=0, stdout="")
                 proj._Reproject(self.REVID)
 
@@ -4110,7 +4127,7 @@
         with utils_for_test.TempGitTree() as tempdir:
             proj = self._get_project(tempdir)
             proj.IsRebaseInProgress.return_value = True
-            with mock.patch("subprocess.run") as mock_run:
+            with mock.patch.object(subprocess, "run") as mock_run:
                 with self.assertRaises(project.LocalSyncFail) as e:
                     proj._Reproject(self.REVID)
             self.assertIn("rebase in progress", str(e.exception))
@@ -4120,9 +4137,9 @@
         """Test a non-zero exit fails the sync with the command's output."""
         with utils_for_test.TempGitTree() as tempdir:
             proj = self._get_project(tempdir)
-            with mock.patch(
-                "git_command.GitCommand", side_effect=self._git_command()
-            ), mock.patch("subprocess.run") as mock_run:
+            with mock.patch.object(
+                git_command, "GitCommand", side_effect=self._git_command()
+            ), mock.patch.object(subprocess, "run") as mock_run:
                 mock_run.return_value = mock.MagicMock(
                     returncode=3, stdout="disk on fire\n"
                 )
@@ -4136,9 +4153,9 @@
         with utils_for_test.TempGitTree() as tempdir:
             proj = self._get_project(tempdir)
             proj.work_git.GetHead.side_effect = [self.HEAD_ID, self.REVID]
-            with mock.patch(
-                "git_command.GitCommand", side_effect=self._git_command()
-            ), mock.patch("subprocess.run") as mock_run:
+            with mock.patch.object(
+                git_command, "GitCommand", side_effect=self._git_command()
+            ), mock.patch.object(subprocess, "run") as mock_run:
                 mock_run.return_value = mock.MagicMock(returncode=0, stdout="")
                 with self.assertRaises(project.LocalSyncFail) as e:
                     proj._Reproject(self.REVID)
@@ -4154,9 +4171,9 @@
                 self.HEAD_ID,  # HEAD before command
                 self.REVID,  # HEAD after command
             ]
-            with mock.patch(
-                "git_command.GitCommand", side_effect=self._git_command()
-            ), mock.patch("subprocess.run") as mock_run:
+            with mock.patch.object(
+                git_command, "GitCommand", side_effect=self._git_command()
+            ), mock.patch.object(subprocess, "run") as mock_run:
                 mock_run.return_value = mock.MagicMock(returncode=0, stdout="")
                 with self.assertRaises(project.LocalSyncFail) as e:
                     proj._Reproject(self.REVID)
@@ -4166,10 +4183,11 @@
         """Test _Reproject does not diff the index against REPO_TREV^{tree}."""
         with utils_for_test.TempGitTree() as tempdir:
             proj = self._get_project(tempdir)
-            with mock.patch(
-                "git_command.GitCommand",
+            with mock.patch.object(
+                git_command,
+                "GitCommand",
                 side_effect=self._git_command(),
-            ) as mock_git_cmd, mock.patch("subprocess.run") as mock_run:
+            ) as mock_git_cmd, mock.patch.object(subprocess, "run") as mock_run:
                 mock_run.return_value = mock.MagicMock(returncode=0, stdout="")
                 proj._Reproject(self.REVID)
 
@@ -4517,7 +4535,7 @@
         """Test _Reproject traces execution and propagates trace OSError."""
         with utils_for_test.TempGitTree() as tempdir:
             proj = self._get_project(tempdir)
-            with mock.patch("repo_trace.Trace") as mock_trace:
+            with mock.patch.object(repo_trace, "Trace") as mock_trace:
                 self._reproject(proj)
 
             mock_trace.assert_called_once_with(
@@ -4528,12 +4546,12 @@
                 self.REVID,
             )
 
-            with mock.patch(
-                "git_command.GitCommand", side_effect=self._git_command()
-            ), mock.patch(
-                "repo_trace.Trace", side_effect=OSError("trace disk full")
-            ), mock.patch(
-                "subprocess.run"
+            with mock.patch.object(
+                git_command, "GitCommand", side_effect=self._git_command()
+            ), mock.patch.object(
+                repo_trace, "Trace", side_effect=OSError("trace disk full")
+            ), mock.patch.object(
+                subprocess, "run"
             ) as mock_run:
                 with self.assertRaises(OSError):
                     proj._Reproject(self.REVID)
@@ -4553,9 +4571,11 @@
                     fp.write("[custom]\n\treprojected = true\n")
                 return mock.MagicMock(returncode=1, stdout="failed\n")
 
-            with mock.patch(
-                "git_command.GitCommand", side_effect=self._git_command()
-            ), mock.patch("subprocess.run", side_effect=_fail_after_edit):
+            with mock.patch.object(
+                git_command, "GitCommand", side_effect=self._git_command()
+            ), mock.patch.object(
+                subprocess, "run", side_effect=_fail_after_edit
+            ):
                 with self.assertRaises(project.LocalSyncFail):
                     proj._Reproject(self.REVID)
 
@@ -5034,10 +5054,10 @@
 
     def test_download_patch_set_with_no_filter_and_no_tags(self) -> None:
         """Test DownloadPatchSet includes --no-filter and --no-tags."""
-        with mock.patch(
-            "git_command.git_require", return_value=True
+        with mock.patch.object(
+            git_command, "git_require", return_value=True
         ) as mock_git_require:
-            with mock.patch("git_command.GitCommand") as mock_git_cmd:
+            with mock.patch.object(git_command, "GitCommand") as mock_git_cmd:
                 mock_cmd_instance = mock.MagicMock()
                 mock_cmd_instance.Wait.return_value = 0
                 mock_git_cmd.return_value = mock_cmd_instance
@@ -5063,8 +5083,8 @@
 
     def test_download_patch_set_legacy_git_omits_no_filter(self) -> None:
         """Test DownloadPatchSet omits --no-filter on git < 2.17."""
-        with mock.patch("git_command.git_require", return_value=False):
-            with mock.patch("git_command.GitCommand") as mock_git_cmd:
+        with mock.patch.object(git_command, "git_require", return_value=False):
+            with mock.patch.object(git_command, "GitCommand") as mock_git_cmd:
                 mock_cmd_instance = mock.MagicMock()
                 mock_cmd_instance.Wait.return_value = 0
                 mock_git_cmd.return_value = mock_cmd_instance
diff --git a/tests/test_ssh.py b/tests/test_ssh.py
index 6bb2c2f..26bd985 100644
--- a/tests/test_ssh.py
+++ b/tests/test_ssh.py
@@ -53,7 +53,9 @@
 
 def test_version() -> None:
     """Check version() handling."""
-    with mock.patch("ssh._run_ssh_version", return_value="OpenSSH_1.2\n"):
+    with mock.patch.object(
+        ssh, "_run_ssh_version", return_value="OpenSSH_1.2\n"
+    ):
         assert ssh.version() == (1, 2)
 
 
@@ -67,7 +69,7 @@
 def test_context_manager_child_cleanup() -> None:
     """Verify orphaned clients & masters get cleaned up."""
     with multiprocessing.Manager() as manager:
-        with mock.patch("ssh.version", return_value=(1, 2)):
+        with mock.patch.object(ssh, "version", return_value=(1, 2)):
             with ssh.ProxyManager(manager) as ssh_proxy:
                 client = subprocess.Popen(["sleep", "964853320"])
                 ssh_proxy.add_client(client)
@@ -87,13 +89,13 @@
         )
 
         # Old ssh version uses port.
-        with mock.patch("ssh.version", return_value=(6, 6)):
+        with mock.patch.object(ssh, "version", return_value=(6, 6)):
             with proxy as ssh_proxy:
                 assert ssh_proxy.sock().endswith("%p")
 
         proxy._sock_path = None
         # New ssh version uses hash.
-        with mock.patch("ssh.version", return_value=(6, 7)):
+        with mock.patch.object(ssh, "version", return_value=(6, 7)):
             with proxy as ssh_proxy:
                 assert ssh_proxy.sock().endswith("%C")
         proxy._sock_path = None
diff --git a/tests/test_subcmds_sync.py b/tests/test_subcmds_sync.py
index 7483f37..01310e7 100644
--- a/tests/test_subcmds_sync.py
+++ b/tests/test_subcmds_sync.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 """Unittests for the subcmds/sync.py module."""
 
+import builtins
 import contextlib
 import json
 import optparse
@@ -35,6 +36,7 @@
 import error
 import git_command
 import git_config
+import git_superproject
 import manifest_xml
 import progress
 from project import SyncNetworkHalfResult
@@ -101,8 +103,8 @@
     cmd = sync.Sync()
     opts, _ = cmd.OptionParser.parse_args(cli_args)
 
-    with mock.patch(
-        "git_superproject.UseSuperproject", return_value=use_superproject
+    with mock.patch.object(
+        git_superproject, "UseSuperproject", return_value=use_superproject
     ):
         assert cmd._GetCurrentBranchOnly(opts, cmd.manifest) == result
 
@@ -726,8 +728,8 @@
         """Common teardown."""
         shutil.rmtree(self.topdir)
 
-    def _new_state(self, time=_TIME):
-        with mock.patch("time.time", return_value=time):
+    def _new_state(self, now: int = _TIME) -> sync.LocalSyncState:
+        with mock.patch.object(time, "time", return_value=now):
             return sync.LocalSyncState(self.manifest)
 
     def test_set(self):
@@ -885,7 +887,7 @@
         self.state = self._new_state()
         self.assertEqual(self.state.GetFetchTime(projA), 5)
         self.assertEqual(self.state.GetFetchTime(projB), 7)
-        with mock.patch("os.path.exists", side_effect=mock_exists):
+        with mock.patch.object(os.path, "exists", side_effect=mock_exists):
             self.state.PruneRemovedProjects()
         self.assertIsNone(self.state.GetFetchTime(projA))
 
@@ -917,7 +919,7 @@
             outer_client=self.manifest,
         )
 
-        with mock.patch("time.time", return_value=self._TIME):
+        with mock.patch.object(time, "time", return_value=self._TIME):
             state = sync.LocalSyncState(child_manifest)
 
         state.SetFetchTime(project)
@@ -954,8 +956,8 @@
         self.state = self._new_state()
         self.assertEqual(self.state.GetFetchTime(projA), 5)
         self.assertEqual(self.state.GetFetchTime(projB), 7)
-        with mock.patch("os.path.exists", side_effect=mock_exists):
-            with mock.patch("os.path.islink", side_effect=mock_islink):
+        with mock.patch.object(os.path, "exists", side_effect=mock_exists):
+            with mock.patch.object(os.path, "islink", side_effect=mock_islink):
                 self.state.PruneRemovedProjects()
         self.assertIsNone(self.state.GetFetchTime(projB))
 
@@ -1394,7 +1396,7 @@
             "ssh_proxy": None,
         }
 
-    @mock.patch("subcmds.sync.Sync.is_multiprocessing_active")
+    @mock.patch.object(sync.Sync, "is_multiprocessing_active")
     def test_fetch_one_keyboard_interrupt_main_process(self, mock_is_active):
         """Test that _FetchOne re-raises KeyboardInterrupt if not worker."""
         mock_is_active.return_value = False
@@ -1408,7 +1410,7 @@
             with self.assertRaises(KeyboardInterrupt):
                 sync.Sync._FetchOne(self.opt, 0)
 
-    @mock.patch("subcmds.sync.Sync.is_multiprocessing_active")
+    @mock.patch.object(sync.Sync, "is_multiprocessing_active")
     def test_fetch_one_keyboard_interrupt_worker_process(self, mock_is_active):
         """Test that _FetchOne suppresses KeyboardInterrupt in workers."""
         mock_is_active.return_value = True
@@ -1641,7 +1643,7 @@
         """Test parallel GC (jobs >= 2)."""
         self.opt.jobs = 2
         with mock.patch.object(sync.Sync, "_SetPreciousObjectsState"):
-            with mock.patch("subcmds.sync._threading.Thread") as mock_thread:
+            with mock.patch.object(threading, "Thread") as mock_thread:
                 mock_t = mock.MagicMock()
                 mock_thread.return_value = mock_t
                 err_event = mock.Mock()
@@ -2271,8 +2273,8 @@
         mock.patch.object(sync, "_PostRepoFetch").start()
 
         # Mock parallel context for worker tests.
-        self.parallel_context_patcher = mock.patch(
-            "subcmds.sync.Sync.get_parallel_context"
+        self.parallel_context_patcher = mock.patch.object(
+            sync.Sync, "get_parallel_context"
         )
         self.mock_get_parallel_context = self.parallel_context_patcher.start()
         self.sync_dict = {}
@@ -2470,8 +2472,8 @@
             self.cmd, "GetProjects", return_value=reloaded_projects
         ), mock.patch.object(self.cmd, "event_log"), mock.patch.object(
             self.cmd, "ExecuteInParallel", side_effect=execute_side_effect
-        ), mock.patch(
-            "subcmds.sync.SyncBuffer"
+        ), mock.patch.object(
+            sync, "SyncBuffer"
         ) as mock_sync_buffer:
             mock_sync_buffer.return_value.Finish.return_value = True
             mock_sync_buffer.return_value.errors = []
@@ -2712,7 +2714,7 @@
         project.manifest.manifestProject.config = mock.MagicMock()
         self.mock_context["projects"] = [project]
 
-        with mock.patch("subcmds.sync.SyncBuffer") as mock_sync_buffer:
+        with mock.patch.object(sync, "SyncBuffer") as mock_sync_buffer:
             mock_sync_buf_instance = mock.MagicMock()
             mock_sync_buf_instance.Finish.return_value = True
             mock_sync_buf_instance.errors = []
@@ -2739,7 +2741,7 @@
             with self.subTest(this_manifest_only=this_manifest_only):
                 opt = self._get_opts()
                 opt.this_manifest_only = this_manifest_only
-                with mock.patch("subcmds.sync.SyncBuffer") as mock_sync_buffer:
+                with mock.patch.object(sync, "SyncBuffer") as mock_sync_buffer:
                     mock_sync_buffer.return_value.Finish.return_value = True
                     mock_sync_buffer.return_value.errors = []
                     result_obj = self.cmd._SyncProjectList(opt, [0])
@@ -2818,7 +2820,7 @@
         project.manifest.manifestProject.config = mock.MagicMock()
         self.mock_context["projects"] = [project]
 
-        with mock.patch("subcmds.sync.SyncBuffer"):
+        with mock.patch.object(sync, "SyncBuffer"):
             result_obj = self.cmd._SyncProjectList(opt, [0])
             result = result_obj.results[0]
 
@@ -2838,7 +2840,7 @@
         project.manifest.manifestProject.config = mock.MagicMock()
         self.mock_context["projects"] = [project]
 
-        with mock.patch("subcmds.sync.SyncBuffer") as mock_sync_buffer:
+        with mock.patch.object(sync, "SyncBuffer") as mock_sync_buffer:
             mock_sync_buf_instance = mock.MagicMock()
             mock_sync_buf_instance.Finish.return_value = True
             mock_sync_buf_instance.errors = []
@@ -3422,10 +3424,10 @@
         self.manifest.manifestProject.GetMergeBranch.return_value = "main"
         self.smart_sync_manifest_path = "/fake/path/to/manifest.xml"
 
-    @mock.patch("shutil.which")
-    @mock.patch("subprocess.Popen")
-    @mock.patch("xmlrpc.client.Server")
-    @mock.patch("subcmds.sync.PersistentTransport")
+    @mock.patch.object(shutil, "which")
+    @mock.patch.object(subprocess, "Popen")
+    @mock.patch.object(xmlrpc.client, "Server")
+    @mock.patch.object(sync, "PersistentTransport")
     def test_smart_sync_setup_with_helper(
         self, mock_transport_class, mock_server_class, mock_popen, mock_which
     ):
@@ -3457,7 +3459,7 @@
         self.cmd._ReloadManifest = mock.MagicMock()
 
         # Mock open to avoid writing to disk
-        with mock.patch("builtins.open", mock.mock_open()):
+        with mock.patch.object(builtins, "open", mock.mock_open()):
             manifest_name = self.cmd._SmartSyncSetup(
                 self.opt, self.smart_sync_manifest_path, self.manifest
             )
@@ -3486,7 +3488,7 @@
 
         self.assertEqual(manifest_name, "manifest.xml")
 
-    @mock.patch("xmlrpc.client.Server")
+    @mock.patch.object(xmlrpc.client, "Server")
     def test_smart_sync_setup_unparsable_manifest(
         self, mock_server_class: mock.MagicMock
     ) -> None:
@@ -3511,14 +3513,14 @@
             side_effect=error.ManifestParseError("mismatched tag")
         )
 
-        with mock.patch("builtins.open", mock.mock_open()):
+        with mock.patch.object(builtins, "open", mock.mock_open()):
             with self.assertRaises(error.ManifestParseError):
                 self.cmd._SmartSyncSetup(
                     self.opt, self.smart_sync_manifest_path, self.manifest
                 )
 
-    @mock.patch("shutil.which")
-    @mock.patch("subprocess.Popen")
+    @mock.patch.object(shutil, "which")
+    @mock.patch.object(subprocess, "Popen")
     def test_smart_sync_setup_helper_error(self, mock_popen, mock_which):
         """Test _SmartSyncSetup when helper returns an error status."""
         self.manifest.manifest_server = (
@@ -3546,7 +3548,7 @@
             str(context.exception),
         )
 
-    @mock.patch("shutil.which")
+    @mock.patch.object(shutil, "which")
     def test_smart_sync_setup_missing_declared_helper(self, mock_which):
         """Test _SmartSyncSetup when helper declared in manifest is missing."""
         self.manifest.manifest_server = (
@@ -3566,8 +3568,8 @@
             str(context.exception),
         )
 
-    @mock.patch("shutil.which")
-    @mock.patch("subprocess.Popen")
+    @mock.patch.object(shutil, "which")
+    @mock.patch.object(subprocess, "Popen")
     def test_smart_sync_setup_helper_exit_code_error(
         self, mock_popen, mock_which
     ):
@@ -3598,8 +3600,8 @@
             str(context.exception),
         )
 
-    @mock.patch("shutil.which")
-    @mock.patch("subprocess.Popen")
+    @mock.patch.object(shutil, "which")
+    @mock.patch.object(subprocess, "Popen")
     def test_smart_sync_setup_helper_json_decode_error_with_stderr(
         self, mock_popen, mock_which
     ):
@@ -3632,8 +3634,8 @@
             str(context.exception),
         )
 
-    @mock.patch("shutil.which")
-    @mock.patch("subprocess.Popen")
+    @mock.patch.object(shutil, "which")
+    @mock.patch.object(subprocess, "Popen")
     def test_smart_sync_setup_helper_error_with_stderr(
         self, mock_popen, mock_which
     ):
@@ -3694,9 +3696,9 @@
         )
 
     def _RunSetup(self, env: Dict[str, str]) -> str:
-        with mock.patch.dict(os.environ, env, clear=True), mock.patch(
-            "xmlrpc.client.Server", return_value=self.mock_server
-        ), mock.patch("builtins.open", mock.mock_open()):
+        with mock.patch.dict(os.environ, env, clear=True), mock.patch.object(
+            xmlrpc.client, "Server", return_value=self.mock_server
+        ), mock.patch.object(builtins, "open", mock.mock_open()):
             return self.cmd._SmartSyncSetup(
                 self.opt, self.smart_sync_manifest_path, self.manifest
             )
diff --git a/tests/test_subcmds_upload.py b/tests/test_subcmds_upload.py
index 53cc651..8968042 100644
--- a/tests/test_subcmds_upload.py
+++ b/tests/test_subcmds_upload.py
@@ -19,7 +19,9 @@
 
 import pytest
 
+import editor
 import error
+import git_superproject
 from subcmds import upload
 
 
@@ -183,8 +185,10 @@
     branch2 = _create_mock_branch("b2", project_relpath="p2")
     pending = [(branch1.project, [branch1]), (branch2.project, [branch2])]
 
-    with mock.patch.object(cmd, "_UploadAndReport") as mock_upload, mock.patch(
-        "editor.Editor.EditString"
+    with mock.patch.object(
+        cmd, "_UploadAndReport"
+    ) as mock_upload, mock.patch.object(
+        editor.Editor, "EditString"
     ) as mock_edit:
         cmd._MultipleBranches(opt, pending, _STUB_PEOPLE)
         mock_edit.assert_not_called()
@@ -202,8 +206,10 @@
     branch2 = _create_mock_branch("b2", project_relpath="p2")
     pending = [(branch1.project, [branch1]), (branch2.project, [branch2])]
 
-    with mock.patch.object(cmd, "_UploadAndReport") as mock_upload, mock.patch(
-        "editor.Editor.EditString"
+    with mock.patch.object(
+        cmd, "_UploadAndReport"
+    ) as mock_upload, mock.patch.object(
+        editor.Editor, "EditString"
     ) as mock_edit:
         cmd._MultipleBranches(opt, pending, _STUB_PEOPLE)
         mock_edit.assert_not_called()
@@ -221,8 +227,10 @@
     branch2 = _create_mock_branch("feature", project_relpath="p2")
     pending = [(branch1.project, [branch1]), (branch2.project, [branch2])]
 
-    with mock.patch.object(cmd, "_UploadAndReport") as mock_upload, mock.patch(
-        "editor.Editor.EditString"
+    with mock.patch.object(
+        cmd, "_UploadAndReport"
+    ) as mock_upload, mock.patch.object(
+        editor.Editor, "EditString"
     ) as mock_edit:
         cmd._MultipleBranches(opt, pending, _STUB_PEOPLE)
         mock_edit.assert_not_called()
@@ -265,8 +273,10 @@
         "  branch b1 ( 1 commit, 2026-08-26) to remote branch "
         "refs/heads/main:\n"
     )
-    with mock.patch.object(cmd, "_UploadAndReport") as mock_upload, mock.patch(
-        "editor.Editor.EditString", return_value=edited_script
+    with mock.patch.object(
+        cmd, "_UploadAndReport"
+    ) as mock_upload, mock.patch.object(
+        editor.Editor, "EditString", return_value=edited_script
     ) as mock_edit:
         cmd._MultipleBranches(opt, pending, _STUB_PEOPLE)
         mock_edit.assert_called_once()
@@ -324,7 +334,9 @@
     cmd.CommonValidateOptions(opt, [])
     branch = mock.MagicMock()
     branch.project.dest_branch = None
-    with mock.patch("git_superproject.UseSuperproject", return_value=False):
+    with mock.patch.object(
+        git_superproject, "UseSuperproject", return_value=False
+    ):
         cmd._UploadBranch(opt, branch, _STUB_PEOPLE)
 
     branch.UploadForReview.assert_called_once()
diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py
index d27b170..82b5425 100644
--- a/tests/test_wrapper.py
+++ b/tests/test_wrapper.py
@@ -83,9 +83,11 @@
     def test_version(self, repo_wrapper: wrapper.Wrapper) -> None:
         """Make sure _Version works."""
         with pytest.raises(SystemExit) as e:
-            with mock.patch("sys.stdout", new_callable=io.StringIO) as stdout:
-                with mock.patch(
-                    "sys.stderr", new_callable=io.StringIO
+            with mock.patch.object(
+                sys, "stdout", new_callable=io.StringIO
+            ) as stdout:
+                with mock.patch.object(
+                    sys, "stderr", new_callable=io.StringIO
                 ) as stderr:
                     repo_wrapper._Version()
         assert e.value.code == 0