Repository: pytest-dev/pyfakefs
Pull request #1132 (2025-03-02 09:33:31)

Fix os.access behavior for symlinks under Windows
- found after enabling symlink tests for Windows

#### Tasks
- [x] Unit tests added that reproduce the issue or prove feature is working
- [x] Fix or feature added
- [x] Entry to release notes added
- [x] Pre-commit CI shows no errors
- [x] Unit tests passing



Patch:
diff --git a/CHANGES.md b/CHANGES.md
index a2916fb..3ac40e4 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -22,6 +22,7 @@ The released versions correspond to PyPI releases.
   (see [#1120](../../issues/1120))
 * fixed a regression that could break tests under Posix in Python 3.12
   (see [#1126](../../issues/1126))
+* fixed behavior for `os.access` for symlinks under Windows
 
 ## [Version 5.7.4](https://pypi.python.org/pypi/pyfakefs/5.7.4) (2025-01-14)
 Minor bugfix release.
diff --git a/pyfakefs/fake_os.py b/pyfakefs/fake_os.py
index 83c4011..59a7690 100644
--- a/pyfakefs/fake_os.py
+++ b/pyfakefs/fake_os.py
@@ -1021,8 +1021,8 @@ class FakeOsModule:
         mode: int,
         *,
         dir_fd: Optional[int] = None,
-        effective_ids: bool = False,
-        follow_symlinks: bool = True,
+        effective_ids: Optional[bool] = None,
+        follow_symlinks: Optional[bool] = None,
     ) -> bool:
         """Check if a file exists and has the specified permissions.
 
@@ -1039,10 +1039,17 @@ class FakeOsModule:
         Returns:
             bool, `True` if file is accessible, `False` otherwise.
         """
-        if effective_ids and self.filesystem.is_windows_fs:
+        if effective_ids is not None and self.filesystem.is_windows_fs:
             raise NotImplementedError(
                 "access: effective_ids unavailable on this platform"
             )
+        if follow_symlinks is None:
+            # different behavior under Windows
+            follow_symlinks = not self.filesystem.is_windows_fs
+        elif self.filesystem.is_windows_fs:
+            raise NotImplementedError(
+                "access: follow_symlinks unavailable on this platform"
+            )
         path = self._path_with_dir_fd(path, self.access, dir_fd)
         try:
             stat_result = self.stat(path, follow_symlinks=follow_symlinks)


Test patch:
diff --git a/pyfakefs/tests/fake_filesystem_shutil_test.py b/pyfakefs/tests/fake_filesystem_shutil_test.py
index 209de51..5a91037 100644
--- a/pyfakefs/tests/fake_filesystem_shutil_test.py
+++ b/pyfakefs/tests/fake_filesystem_shutil_test.py
@@ -27,7 +27,7 @@ from pathlib import Path
 
 from pyfakefs import fake_filesystem_unittest
 from pyfakefs.helpers import get_uid, set_uid, is_root, IS_PYPY
-from pyfakefs.tests.test_utils import RealFsTestMixin
+from pyfakefs.tests.test_utils import RealFsTestMixin, skip_if_symlink_not_supported
 
 is_windows = sys.platform == "win32"
 
@@ -244,7 +244,7 @@ class FakeShutilModuleTest(RealFsTestCase):
     @unittest.skipIf(IS_PYPY, "Functionality not supported in PyPy")
     def test_copystat_symlinks(self):
         """Regression test for #799"""
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         f = self.make_path("xyzzy")
         self.create_file(f)
         sym1 = self.make_path("sym1")
@@ -434,7 +434,7 @@ class FakeCopyFileTest(RealFsTestCase):
             shutil.copyfile(src_file, dst_file)
 
     def test_raises_if_dest_is_a_symlink_to_src(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         src_file = self.make_path("foo")
         dst_file = self.make_path("bar")
         contents = "contents of file"
diff --git a/pyfakefs/tests/fake_filesystem_test.py b/pyfakefs/tests/fake_filesystem_test.py
index 9af21ee..d851d05 100644
--- a/pyfakefs/tests/fake_filesystem_test.py
+++ b/pyfakefs/tests/fake_filesystem_test.py
@@ -34,7 +34,12 @@ from pyfakefs.fake_filesystem import (
     OSType,
 )
 from pyfakefs.helpers import IS_WIN
-from pyfakefs.tests.test_utils import TestCase, RealFsTestCase, time_mock
+from pyfakefs.tests.test_utils import (
+    TestCase,
+    RealFsTestCase,
+    time_mock,
+    skip_if_symlink_not_supported,
+)
 
 
 class FakeDirectoryUnitTest(TestCase):
@@ -2039,7 +2044,7 @@ class MountPointTest(TestCase):
 
 class ConvenienceMethodTest(RealFsTestCase):
     def test_create_link_with_non_existent_parent(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file1_path = self.make_path("test_file1")
         link_path = self.make_path("nonexistent", "test_file2")
 
@@ -2050,7 +2055,7 @@ class ConvenienceMethodTest(RealFsTestCase):
         self.assertTrue(self.filesystem.exists(link_path))
 
     def test_create_symlink_with_non_existent_parent(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file1_path = self.make_path("test_file1")
         link_path = self.make_path("nonexistent", "test_file2")
 
@@ -2163,7 +2168,7 @@ class RealFileSystemAccessTest(RealFsTestCase):
                 self.filesystem.add_real_directory(root_dir, target_path="/root/")
 
     def test_symlink_is_merged(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.filesystem.create_dir(os.path.join("/", "root", "foo"))
         with self.create_real_paths() as root_dir:
             link_path = os.path.join(root_dir, "link.txt")
@@ -2428,7 +2433,7 @@ class RealFileSystemAccessTest(RealFsTestCase):
         )
 
     def test_add_existing_real_directory_symlink_target_path(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         real_directory = self._setup_temp_directory()
         symlinks = [
             (
@@ -2453,7 +2458,7 @@ class RealFileSystemAccessTest(RealFsTestCase):
         self.assertTrue(self.filesystem.exists("/path/fixtures/symlink_file_relative"))
 
     def test_add_existing_real_directory_symlink_lazy_read(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         real_directory = self._setup_temp_directory()
         symlinks = [
             (
diff --git a/pyfakefs/tests/fake_filesystem_vs_real_test.py b/pyfakefs/tests/fake_filesystem_vs_real_test.py
index c0dc5ee..7ca4281 100644
--- a/pyfakefs/tests/fake_filesystem_vs_real_test.py
+++ b/pyfakefs/tests/fake_filesystem_vs_real_test.py
@@ -23,7 +23,7 @@ import time
 import unittest
 
 from pyfakefs import fake_filesystem, fake_os, fake_open
-from pyfakefs.helpers import IS_PYPY
+from pyfakefs.tests.test_utils import skip_if_symlink_not_supported
 
 
 def sep(path):
@@ -81,6 +81,7 @@ class FakeFilesystemVsRealTest(TestCase):
             fh.close()
         # l for symlink, h for hard link
         if file_type in ("l", "h"):
+            contents = sep(contents)
             real_target, fake_target = (contents, contents)
             # If it begins with '/', make it relative to the base. You can't go
             # creating files in / for the real file system.
@@ -375,13 +376,16 @@ class FakeFilesystemVsRealTest(TestCase):
 
     def assertAllOsBehaviorsMatch(self, path):
         path = sep(path)
-        os_method_names = [] if self.is_windows else ["readlink"]
+        os_method_names = ["readlink"]
         os_method_names_no_args = ["getcwd"]
-        os_path_method_names = ["isabs", "isdir"]
-        if not self.is_windows:
-            os_path_method_names += ["islink", "lexists"]
-        if not self.is_windows or not IS_PYPY:
-            os_path_method_names += ["isfile", "exists"]
+        os_path_method_names = [
+            "isabs",
+            "isdir",
+            "islink",
+            "lexists",
+            "isfile",
+            "exists",
+        ]
 
         wrapped_methods = [
             ["access", self._access_real, self._access_fake],
@@ -557,34 +561,33 @@ class FakeFilesystemVsRealTest(TestCase):
         self._create_test_file("b", "aFile", b"some contents")
         self.assertAllOsBehaviorsMatch("aFile")
 
-    @unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
     def test_sym_link_to_empty_file(self):
+        skip_if_symlink_not_supported()
         self._create_test_file("f", "aFile")
         self._create_test_file("l", "link_to_empty", "aFile")
         self.assertAllOsBehaviorsMatch("link_to_empty")
 
-    @unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
     def test_hard_link_to_empty_file(self):
+        skip_if_symlink_not_supported()
         self._create_test_file("f", "aFile")
         self._create_test_file("h", "link_to_empty", "aFile")
         self.assertAllOsBehaviorsMatch("link_to_empty")
 
-    @unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
     def test_sym_link_to_real_file(self):
+        skip_if_symlink_not_supported()
         self._create_test_file("f", "aFile", "some contents")
         self._create_test_file("l", "link_to_file", "aFile")
         self.assertAllOsBehaviorsMatch("link_to_file")
 
-    @unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
     def test_hard_link_to_real_file(self):
+        skip_if_symlink_not_supported()
         self._create_test_file("f", "aFile", "some contents")
         self._create_test_file("h", "link_to_file", "aFile")
         self.assertAllOsBehaviorsMatch("link_to_file")
 
-    @unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
     def test_broken_sym_link(self):
+        skip_if_symlink_not_supported()
         self._create_test_file("l", "broken_link", "broken")
-        self._create_test_file("l", "loop", "/a/loop")
         self.assertAllOsBehaviorsMatch("broken_link")
 
     def test_file_in_a_folder(self):
@@ -593,16 +596,16 @@ class FakeFilesystemVsRealTest(TestCase):
         self._create_test_file("f", "a/b/file", "contents")
         self.assertAllOsBehaviorsMatch("a/b/file")
 
-    @unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
     def test_absolute_sym_link_to_folder(self):
+        skip_if_symlink_not_supported()
         self._create_test_file("d", "a")
         self._create_test_file("d", "a/b")
         self._create_test_file("f", "a/b/file", "contents")
         self._create_test_file("l", "a/link", "/a/b")
         self.assertAllOsBehaviorsMatch("a/link/file")
 
-    @unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
     def test_link_to_folder_after_chdir(self):
+        skip_if_symlink_not_supported()
         self._create_test_file("d", "a")
         self._create_test_file("d", "a/b")
         self._create_test_file("f", "a/b/file", "contents")
@@ -613,33 +616,33 @@ class FakeFilesystemVsRealTest(TestCase):
         self.fake_os.chdir(fake_dir)
         self.assertAllOsBehaviorsMatch("file")
 
-    @unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
     def test_relative_sym_link_to_folder(self):
+        skip_if_symlink_not_supported()
         self._create_test_file("d", "a")
         self._create_test_file("d", "a/b")
         self._create_test_file("f", "a/b/file", "contents")
         self._create_test_file("l", "a/link", "b")
         self.assertAllOsBehaviorsMatch("a/link/file")
 
-    @unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
     def test_sym_link_to_parent(self):
+        skip_if_symlink_not_supported()
         # Soft links on HFS+ / OS X behave differently.
-        if os.uname()[0] != "Darwin":
+        if sys.platform != "darwin":
             self._create_test_file("d", "a")
             self._create_test_file("d", "a/b")
             self._create_test_file("l", "a/b/c", "..")
             self.assertAllOsBehaviorsMatch("a/b/c")
 
-    @unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
     def test_path_through_sym_link_to_parent(self):
+        skip_if_symlink_not_supported()
         self._create_test_file("d", "a")
         self._create_test_file("f", "a/target", "contents")
         self._create_test_file("d", "a/b")
         self._create_test_file("l", "a/b/c", "..")
         self.assertAllOsBehaviorsMatch("a/b/c/target")
 
-    @unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
     def test_sym_link_to_sibling_directory(self):
+        skip_if_symlink_not_supported()
         self._create_test_file("d", "a")
         self._create_test_file("d", "a/b")
         self._create_test_file("d", "a/sibling_of_b")
@@ -647,8 +650,8 @@ class FakeFilesystemVsRealTest(TestCase):
         self._create_test_file("l", "a/b/c", "../sibling_of_b")
         self.assertAllOsBehaviorsMatch("a/b/c/target")
 
-    @unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
-    def test_sym_link_to_sibling_directory_non_existant_file(self):
+    def test_sym_link_to_sibling_directory_non_existent_file(self):
+        skip_if_symlink_not_supported()
         self._create_test_file("d", "a")
         self._create_test_file("d", "a/b")
         self._create_test_file("d", "a/sibling_of_b")
@@ -656,8 +659,8 @@ class FakeFilesystemVsRealTest(TestCase):
         self._create_test_file("l", "a/b/c", "../sibling_of_b")
         self.assertAllOsBehaviorsMatch("a/b/c/file_does_not_exist")
 
-    @unittest.skipIf(TestCase.is_windows, "no symlink in Windows")
     def test_broken_sym_link_to_sibling_directory(self):
+        skip_if_symlink_not_supported()
         self._create_test_file("d", "a")
         self._create_test_file("d", "a/b")
         self._create_test_file("d", "a/sibling_of_b")
diff --git a/pyfakefs/tests/fake_open_test.py b/pyfakefs/tests/fake_open_test.py
index d760e0a..c1a4abe 100644
--- a/pyfakefs/tests/fake_open_test.py
+++ b/pyfakefs/tests/fake_open_test.py
@@ -28,7 +28,7 @@ from pyfakefs.helpers import is_root, IS_PYPY, get_locale_encoding
 from pyfakefs.fake_io import FakeIoModule
 from pyfakefs.fake_filesystem_unittest import PatchMode, Patcher
 from pyfakefs.tests.skipped_pathlib import read_open
-from pyfakefs.tests.test_utils import RealFsTestCase
+from pyfakefs.tests.test_utils import RealFsTestCase, skip_if_symlink_not_supported
 
 
 class FakeFileOpenTestBase(RealFsTestCase):
@@ -241,7 +241,7 @@ class FakeFileOpenTest(FakeFileOpenTestBase):
         self.assertEqual(contents, result)
 
     def test_exclusive_create_file_failure(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_path = self.make_path("bar")
         self.create_file(file_path)
         self.assert_raises_os_error(errno.EEXIST, self.open, file_path, "x")
@@ -476,7 +476,7 @@ class FakeFileOpenTest(FakeFileOpenTestBase):
             self.open(file_path, "w+", encoding="utf8").close()
 
     def test_follow_link_read(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("foo", "bar", "baz")
         target = self.make_path("tarJAY")
         target_contents = "real baz contents"
@@ -489,7 +489,7 @@ class FakeFileOpenTest(FakeFileOpenTestBase):
         self.assertEqual(target_contents, got_contents)
 
     def test_follow_link_write(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("foo", "bar", "TBD")
         target = self.make_path("tarJAY")
         target_contents = "real baz contents"
@@ -504,7 +504,7 @@ class FakeFileOpenTest(FakeFileOpenTestBase):
 
     def test_follow_intra_path_link_write(self):
         # Test a link in the middle of of a file path.
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.os.path.join(
             self.base_path, "foo", "build", "local_machine", "output", "1"
         )
@@ -1920,7 +1920,7 @@ class ResolvePathTest(FakeFileOpenTestBase):
         self.assertTrue(self.os.path.exists(file_path))
 
     def test_link_within_same_directory(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         final_target = self.make_path("foo", "baz")
         link_path = self.make_path("foo", "bar")
         self.create_symlink(link_path, "baz")
@@ -1929,7 +1929,7 @@ class ResolvePathTest(FakeFileOpenTestBase):
         self.assertEqual(1, self.os.stat(final_target)[stat.ST_SIZE])
 
     def test_link_to_sub_directory(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         final_target = self.make_path("foo", "baz", "bip")
         dir_path = self.make_path("foo", "baz")
         self.create_dir(dir_path)
@@ -1944,7 +1944,7 @@ class ResolvePathTest(FakeFileOpenTestBase):
         self.assertTrue(self.os.stat(dir_path)[stat.ST_MODE] & stat.S_IFDIR)
 
     def test_link_to_parent_directory(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         final_target = self.make_path("baz", "bip")
         self.create_dir(self.make_path("foo"))
         self.create_dir(self.make_path("baz"))
@@ -1956,7 +1956,7 @@ class ResolvePathTest(FakeFileOpenTestBase):
         self.assertTrue(self.os.path.exists(link_path))
 
     def test_link_to_absolute_path(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         final_target = self.make_path("foo", "baz", "bip")
         self.create_dir(self.make_path("foo", "baz"))
         link_path = self.make_path("foo", "bar")
@@ -1965,7 +1965,7 @@ class ResolvePathTest(FakeFileOpenTestBase):
         self.assertTrue(self.os.path.exists(final_target))
 
     def test_relative_links_work_after_chdir(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         final_target = self.make_path("foo", "baz", "bip")
         self.create_dir(self.make_path("foo", "baz"))
         link_path = self.make_path("foo", "bar")
@@ -1984,7 +1984,7 @@ class ResolvePathTest(FakeFileOpenTestBase):
         self.assertTrue(self.os.path.exists(final_target))
 
     def test_absolute_links_work_after_chdir(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         final_target = self.make_path("foo", "baz", "bip")
         self.create_dir(self.make_path("foo", "baz"))
         link_path = self.make_path("foo", "bar")
@@ -2036,7 +2036,7 @@ class ResolvePathTest(FakeFileOpenTestBase):
     def test_read_link_to_link(self):
         # Write into the final link target and read back from a file which will
         # point to that.
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("foo", "bar")
         self.create_symlink(link_path, "link")
         self.create_symlink(self.make_path("foo", "link"), "baz")
@@ -2045,7 +2045,7 @@ class ResolvePathTest(FakeFileOpenTestBase):
         self.assertEqual("x", fh.read())
 
     def test_write_link_to_link(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         final_target = self.make_path("foo", "baz")
         link_path = self.make_path("foo", "bar")
         self.create_symlink(link_path, "link")
@@ -2054,7 +2054,7 @@ class ResolvePathTest(FakeFileOpenTestBase):
         self.assertTrue(self.os.path.exists(final_target))
 
     def test_multiple_links(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.os.makedirs(self.make_path("a", "link1", "c", "link2"))
 
         self.create_symlink(self.make_path("a", "b"), "link1")
@@ -2081,7 +2081,7 @@ class ResolvePathTest(FakeFileOpenTestBase):
 
     def test_utime_link(self):
         """os.utime() and os.stat() via symbolic link (issue #49)"""
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.create_dir(self.make_path("foo", "baz"))
         target_path = self.make_path("foo", "baz", "bip")
         self.write_to_file(target_path)
diff --git a/pyfakefs/tests/fake_os_test.py b/pyfakefs/tests/fake_os_test.py
index 6395248..9a0904c 100644
--- a/pyfakefs/tests/fake_os_test.py
+++ b/pyfakefs/tests/fake_os_test.py
@@ -29,7 +29,11 @@ from pyfakefs.fake_filesystem import (
     set_gid,
 )
 from pyfakefs.helpers import IN_DOCKER, IS_PYPY, get_uid, get_gid, reset_ids
-from pyfakefs.tests.test_utils import TestCase, RealFsTestCase
+from pyfakefs.tests.test_utils import (
+    TestCase,
+    RealFsTestCase,
+    skip_if_symlink_not_supported,
+)
 
 
 class FakeOsModuleTestBase(RealFsTestCase):
@@ -154,7 +158,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         self.assertEqual(["foo"], self.os.listdir(directory))
 
     def test_listdir_on_symlink(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         directory = self.make_path("xyzzy")
         files = ["foo", "bar", "baz"]
         for f in files:
@@ -368,7 +372,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
     def test_stat_no_follow_symlinks_windows(self):
         """Test that stat with follow_symlinks=False behaves like lstat."""
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         directory = self.make_path("xyzzy")
         base_name = "plugh"
         file_contents = "frobozz"
@@ -402,7 +406,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_lstat_size_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         directory = self.make_path("xyzzy")
         base_name = "plugh"
         file_contents = "frobozz"
@@ -474,7 +478,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
     def test_lstat_symlink_with_trailing_sep_linux(self):
         # regression test for #366
         self.check_linux_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("foo")
         self.os.symlink(self.base_path, link_path)
         # used to raise
@@ -483,7 +487,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
     def test_lstat_symlink_with_trailing_sep_macos(self):
         # regression test for #366
         self.check_macos_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("foo")
         self.os.symlink(self.base_path, link_path)
         # used to raise
@@ -491,7 +495,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_readlink_ending_with_sep_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("foo")
         self.os.symlink(self.base_path, link_path)
         self.assert_equal_paths(
@@ -500,7 +504,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_islink_with_trailing_sep_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("foo")
         self.os.symlink(self.base_path, link_path)
         self.assertTrue(self.os.path.islink(link_path + self.os.path.sep))
@@ -548,11 +552,11 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_remove_link_ending_with_sep_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.check_remove_link_ending_with_sep(errno.EACCES)
 
     def test_lstat_uses_open_fd_as_path(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         if os.lstat not in os.supports_fd:
             self.skip_real_fs()
         self.assert_raises_os_error(errno.EBADF, self.os.lstat, 5)
@@ -615,7 +619,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     @unittest.skipIf(not hasattr(os, "O_NOFOLLOW"), "NOFOLLOW attribute not supported")
     def test_open_nofollow_symlink_raises(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_path = self.make_path("file.txt")
         self.create_file(file_path, contents="foo")
         link_path = self.make_path("link")
@@ -626,7 +630,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     @unittest.skipIf(not hasattr(os, "O_NOFOLLOW"), "NOFOLLOW attribute not supported")
     def test_open_nofollow_symlink_as_parent_works(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         dir_path = self.make_path("dir")
         self.create_dir(dir_path)
         link_path = self.make_path("link")
@@ -639,7 +643,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_lexists_with_trailing_separator_linux_windows(self):
         self.check_linux_and_windows()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_path = self.make_path("foo")
         self.os.symlink(file_path, file_path)
         self.assertFalse(self.os.path.lexists(file_path + self.os.sep))
@@ -653,7 +657,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_islink_with_trailing_separator_linux_windows(self):
         self.check_linux_and_windows()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_path = self.make_path("foo")
         self.os.symlink(file_path, file_path)
         self.assertFalse(self.os.path.islink(file_path + self.os.sep))
@@ -712,7 +716,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         self.check_remove_with_trailing_separator(errno.EINVAL)
 
     def test_readlink(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("foo", "bar", "baz")
         target = self.make_path("tarJAY")
         self.create_symlink(link_path, target)
@@ -725,7 +729,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_readlink_raises_if_path_is_not_a_link_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.check_readlink_raises_if_path_is_not_a_link()
 
     def test_readlink_raises_if_path_is_not_a_link_posix(self):
@@ -741,7 +745,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_readlink_raises_if_path_has_file_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.check_readlink_raises_if_path_has_file(errno.ENOENT)
 
     def test_readlink_raises_if_path_has_file_posix(self):
@@ -749,13 +753,13 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         self.check_readlink_raises_if_path_has_file(errno.ENOTDIR)
 
     def test_readlink_raises_if_path_does_not_exist(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.assert_raises_os_error(
             errno.ENOENT, self.os.readlink, "/this/path/does/not/exist"
         )
 
     def test_readlink_raises_if_path_is_none(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         with self.assertRaises(TypeError):
             self.os.readlink(None)
 
@@ -781,7 +785,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_broken_symlink_with_trailing_separator_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_path = self.make_path("foo")
         link_path = self.make_path("link")
         self.os.symlink(file_path, link_path)
@@ -811,7 +815,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
     def test_circular_readlink_with_trailing_separator_windows(self):
         # Regression test for #372
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_path = self.make_path("foo")
         self.os.symlink(file_path, file_path)
         self.assert_raises_os_error(
@@ -819,7 +823,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         )
 
     def test_readlink_with_links_in_path(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.create_symlink(
             self.make_path("meyer", "lemon", "pie"), self.make_path("yum")
         )
@@ -830,7 +834,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         )
 
     def test_readlink_with_chained_links_in_path(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.create_symlink(
             self.make_path("eastern", "european", "wolfhounds", "chase"),
             self.make_path("cats"),
@@ -988,7 +992,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         self.check_remove_dir_raises_error(errno.EACCES)
 
     def test_remove_symlink_to_dir(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         directory = self.make_path("zzy")
         link = self.make_path("link_to_dir")
         self.create_dir(directory)
@@ -1028,7 +1032,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_rename_dir_to_symlink_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("link")
         dir_path = self.make_path("dir")
         link_target = self.os.path.join(dir_path, "link_target")
@@ -1072,7 +1076,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         )
 
     def check_rename_case_with_symlink(self, result):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.check_case_insensitive_fs()
         dir_path_lower = self.make_path("beta")
         self.create_dir(dir_path_lower)
@@ -1199,7 +1203,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_rename_to_a_hardlink_of_same_file_should_do_nothing(self):
         self.skip_real_fs_failure(skip_posix=False)
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_path = self.make_path("dir", "file")
         self.create_file(file_path)
         link_path = self.make_path("link")
@@ -1209,7 +1213,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         self.assertTrue(self.os.path.exists(link_path))
 
     def test_hardlink_works_with_symlink(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         base_path = self.make_path("foo")
         self.create_dir(base_path)
         symlink_path = self.os.path.join(base_path, "slink")
@@ -1523,7 +1527,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_rmdir_via_symlink(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         base_path = self.make_path("foo", "bar")
         dir_path = self.os.path.join(base_path, "alpha")
         self.create_dir(dir_path)
@@ -1677,7 +1681,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_mkdir_removes_symlink_in_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         base_path = self.make_path("foo", "bar")
         link_path = self.os.path.join(base_path, "link_to_dir")
         dir_path = self.os.path.join(base_path, "dir")
@@ -1826,7 +1830,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         )
 
     def test_makedirs_raises_if_parent_is_looping_link(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("link")
         link_target = self.os.path.join(link_path, "link")
         self.os.symlink(link_target, link_path)
@@ -2001,7 +2005,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
             self.assertFalse(self.os.access(path, self.rw))
 
     def test_access_symlink(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.skip_real_fs()
         path = self.make_path("some_file")
         self.createTestFile(path)
@@ -2021,16 +2025,25 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         self.assertFalse(self.os.access(link_path, self.os.X_OK))
         self.assertFalse(self.os.access(link_path, self.rwx))
 
-        # test link itself
-        self.assertTrue(self.os.access(link_path, self.os.F_OK, follow_symlinks=False))
-        self.assertTrue(self.os.access(link_path, self.os.R_OK, follow_symlinks=False))
-        self.assertTrue(self.os.access(link_path, self.os.W_OK, follow_symlinks=False))
+        # test link itself (default under Windows, follow_symlinks not supported)
+        follow_symlinks = None if self.is_windows_fs else False
+        self.assertTrue(
+            self.os.access(link_path, self.os.F_OK, follow_symlinks=follow_symlinks)
+        )
+        self.assertTrue(
+            self.os.access(link_path, self.os.R_OK, follow_symlinks=follow_symlinks)
+        )
+        self.assertTrue(
+            self.os.access(link_path, self.os.W_OK, follow_symlinks=follow_symlinks)
+        )
         if not self.is_windows_fs:
             self.assertTrue(
                 self.os.access(link_path, self.os.X_OK, follow_symlinks=False)
             )
             self.assertTrue(self.os.access(link_path, self.rwx, follow_symlinks=False))
-        self.assertTrue(self.os.access(link_path, self.rw, follow_symlinks=False))
+        self.assertTrue(
+            self.os.access(link_path, self.rw, follow_symlinks=follow_symlinks)
+        )
 
     def test_access_non_existent_file(self):
         # set up
@@ -2044,11 +2057,13 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         self.assertFalse(self.os.access(path, self.rwx))
         self.assertFalse(self.os.access(path, self.rw))
 
-    def test_effective_ids_not_supported_under_windows(self):
+    def test_arguments_not_supported_under_windows(self):
         self.check_windows_only()
         path = self.make_path("foo", "bar")
         with self.assertRaises(NotImplementedError):
             self.os.access(path, self.os.F_OK, effective_ids=True)
+        with self.assertRaises(NotImplementedError):
+            self.os.access(path, self.os.F_OK, follow_symlinks=False)
 
     def test_chmod(self):
         # set up
@@ -2114,7 +2129,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         if sys.version_info < (3, 13):
             self.check_posix_only()
         else:
-            self.skip_if_symlink_not_supported()
+            skip_if_symlink_not_supported()
         path = self.make_path("some_file")
         self.createTestFile(path)
         link_path = self.make_path("link_to_some_file")
@@ -2372,7 +2387,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         self.assert_raises_os_error(errno.ENOTDIR, self.os.mknod, filename2)
 
     def test_symlink(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_path = self.make_path("foo", "bar", "baz")
         self.create_dir(self.make_path("foo", "bar"))
         self.os.symlink("bogus", file_path)
@@ -2410,7 +2425,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_symlink_with_path_ending_with_sep_in_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         dir_path = self.make_path("dir")
         self.create_dir(dir_path)
         self.assert_raises_os_error(
@@ -2432,7 +2447,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_broken_symlink_with_trailing_sep_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         path0 = self.make_path("foo") + self.os.sep
         self.assert_raises_os_error(errno.EINVAL, self.os.symlink, path0, path0)
 
@@ -2454,7 +2469,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_rename_symlink_with_trailing_sep_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         path = self.make_path("foo")
         self.os.symlink(self.base_path, path)
         self.assert_raises_os_error(
@@ -2463,7 +2478,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_rename_symlink_to_other_case(self):
         # Regression test for #389
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("foo")
         self.os.symlink(self.base_path, link_path)
         link_to_link_path = self.make_path("BAR")
@@ -2474,7 +2489,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def create_broken_link_path_with_trailing_sep(self):
         # Regression tests for #396
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("link")
         target_path = self.make_path("target")
         self.os.symlink(target_path, link_path)
@@ -2568,7 +2583,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_rename_link_with_trailing_sep_to_self_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         path = self.make_path("foo")
         self.os.symlink(self.base_path, path)
         self.os.rename(path + self.os.sep, path)  # no error
@@ -2584,7 +2599,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def check_open_broken_symlink_to_path_with_trailing_sep(self, error):
         # Regression tests for #397
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         target_path = self.make_path("target") + self.os.sep
         link_path = self.make_path("link")
         self.os.symlink(target_path, link_path)
@@ -2605,7 +2620,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def check_link_path_ending_with_sep(self, error):
         # Regression tests for #399
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_path = self.make_path("foo")
         link_path = self.make_path("link")
         with self.open(file_path, "w", encoding="utf8"):
@@ -2631,7 +2646,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_link_to_path_ending_with_sep_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         path0 = self.make_path("foo") + self.os.sep
         path1 = self.make_path("bar")
         with self.open(path1, "w", encoding="utf8"):
@@ -2676,7 +2691,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_rmdir_link_with_trailing_sep_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         dir_path = self.make_path("foo")
         self.os.mkdir(dir_path)
         link_path = self.make_path("link")
@@ -2703,7 +2718,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_readlink_circular_link_with_trailing_sep_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         path1 = self.make_path("foo")
         path0 = self.make_path("bar")
         self.os.symlink(path0, path1)
@@ -2713,13 +2728,13 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
     # hard link related tests
     def test_link_bogus(self):
         # trying to create a link from a non-existent file should fail
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.assert_raises_os_error(
             errno.ENOENT, self.os.link, "/nonexistent_source", "/link_dest"
         )
 
     def test_link_delete(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
 
         file1_path = self.make_path("test_file1")
         file2_path = self.make_path("test_file2")
@@ -2736,7 +2751,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
             self.assertEqual(f.read(), contents1)
 
     def test_link_update(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
 
         file1_path = self.make_path("test_file1")
         file2_path = self.make_path("test_file2")
@@ -2756,7 +2771,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
             self.assertEqual(f.read(), contents2)
 
     def test_link_non_existent_parent(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file1_path = self.make_path("test_file1")
         breaking_link_path = self.make_path("nonexistent", "test_file2")
         contents1 = "abcdef"
@@ -2769,14 +2784,14 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
         )
 
     def test_link_is_existing_file(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_path = self.make_path("foo", "bar")
         self.create_file(file_path)
         self.assert_raises_os_error(errno.EEXIST, self.os.link, file_path, file_path)
 
     def test_link_target_is_dir_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         dir_path = self.make_path("foo", "bar")
         link_path = self.os.path.join(dir_path, "link")
         self.create_dir(dir_path)
@@ -2791,7 +2806,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     def test_link_count1(self):
         """Test that hard link counts are updated correctly."""
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file1_path = self.make_path("test_file1")
         file2_path = self.make_path("test_file2")
         file3_path = self.make_path("test_file3")
@@ -2817,7 +2832,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     @unittest.skipIf(IS_PYPY, "follow_symlinks not supported in PyPi")
     def test_link_no_follow_symlink(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         target_path = self.make_path("target_path")
         self.create_file(target_path, contents="foo")
         symlink_path = self.make_path("symlink_to_file")
@@ -2828,7 +2843,7 @@ class FakeOsModuleTest(FakeOsModuleTestBase):
 
     @unittest.skipIf(not IS_PYPY, "follow_symlinks only not supported in PyPi")
     def test_link_follow_symlink_not_supported_inPypy(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         target_path = self.make_path("target_path")
         self.create_file(target_path, contents="foo")
         symlink_path = self.make_path("symlink_to_file")
@@ -3125,7 +3140,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
         self.assertEqual(["foo"], self.os.listdir(directory_upper))
 
     def test_listdir_on_symlink(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         directory = self.make_path("xyzzy")
         files = ["foo", "bar", "baz"]
         for f in files:
@@ -3230,7 +3245,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
         self.assertEqual(len(base_name), self.os.lstat(link_path.upper())[stat.ST_SIZE])
 
     def test_readlink(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("foo", "bar", "baz")
         target = self.make_path("tarJAY")
         self.create_symlink(link_path, target)
@@ -3243,7 +3258,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
 
     def test_readlink_raises_if_path_not_a_link_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.check_readlink_raises_if_path_not_a_link()
 
     def test_readlink_raises_if_path_not_a_link_posix(self):
@@ -3259,7 +3274,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
 
     def test_readlink_raises_if_path_has_file_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.check_readlink_raises_if_path_has_file(errno.ENOENT)
 
     def test_readlink_raises_if_path_has_file_posix(self):
@@ -3267,7 +3282,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
         self.check_readlink_raises_if_path_has_file(errno.ENOTDIR)
 
     def test_readlink_with_links_in_path(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.create_symlink(
             self.make_path("meyer", "lemon", "pie"), self.make_path("yum")
         )
@@ -3278,7 +3293,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
         )
 
     def test_readlink_with_chained_links_in_path(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.create_symlink(
             self.make_path("eastern", "european", "wolfhounds", "chase"),
             self.make_path("cats"),
@@ -3381,7 +3396,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
         self.check_remove_dir_raises_error(errno.EACCES)
 
     def test_remove_symlink_to_dir(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         directory = self.make_path("zzy")
         link = self.make_path("link_to_dir")
         self.create_dir(directory)
@@ -3403,7 +3418,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
 
     def test_rename_dir_to_symlink_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("link")
         dir_path = self.make_path("dir")
         link_target = self.os.path.join(dir_path, "link_target")
@@ -3461,7 +3476,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
 
     def test_rename_directory_to_linked_dir(self):
         # Regression test for #314
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("link")
         self.os.symlink(self.base_path, link_path)
         link_subdir = self.os.path.join(link_path, "dir")
@@ -3503,7 +3518,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
 
     def test_rename_looping_symlink(self):
         # Regression test for #315
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         path_lower = self.make_path("baz")
         path_upper = self.make_path("BAZ")
         self.os.symlink(path_lower, path_upper)
@@ -3557,7 +3572,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
 
     def test_rename_symlink_with_changed_case(self):
         # Regression test for #313
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("link")
         self.os.symlink(self.base_path, link_path)
         link_path = self.os.path.join(link_path, "link")
@@ -3609,7 +3624,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
 
     def test_rename_to_a_hardlink_of_same_file_should_do_nothing(self):
         self.skip_real_fs_failure(skip_posix=False)
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_path = self.make_path("dir", "file")
         self.create_file(file_path)
         link_path = self.make_path("link")
@@ -3639,7 +3654,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
 
     def test_rename_symlink_to_other_case_works_in_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         path0 = self.make_path("beta")
         self.os.symlink(self.base_path, path0)
         path0 = self.make_path("beta", "Beta")
@@ -3671,7 +3686,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
 
     def test_stat_with_mixed_case(self):
         # Regression test for #310
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         base_path = self.make_path("foo")
         path = self.os.path.join(base_path, "bar")
         self.create_dir(path)
@@ -3682,7 +3697,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
         self.os.stat(path)
 
     def test_hardlink_works_with_symlink(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         base_path = self.make_path("foo")
         self.create_dir(base_path)
         symlink_path = self.os.path.join(base_path, "slink")
@@ -3802,7 +3817,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
 
     def test_rename_case_only_with_symlink_parent_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.check_rename_case_only_with_symlink_parent()
 
     def test_rename_case_only_with_symlink_parent_macos(self):
@@ -3858,7 +3873,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
 
     def test_rmdir_via_symlink(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         base_path = self.make_path("foo", "bar")
         dir_path = self.os.path.join(base_path, "alpha")
         self.create_dir(dir_path)
@@ -3891,7 +3906,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
 
     def test_mkdir_removes_symlink_in_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         base_path = self.make_path("foo", "bar")
         link_path = self.os.path.join(base_path, "link_to_dir")
         dir_path = self.os.path.join(base_path, "dir")
@@ -3918,7 +3933,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
 
     def test_mkdir_raises_if_symlink_exists(self):
         # Regression test for #309
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         path1 = self.make_path("baz")
         self.os.symlink(path1, path1)
         path2 = self.make_path("Baz")
@@ -4013,7 +4028,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
         self.assertFalse(st.st_mode & stat.S_IFDIR)
 
     def test_symlink(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_path = self.make_path("foo", "bar", "baz")
         self.create_dir(self.make_path("foo", "bar"))
         self.os.symlink("bogus", file_path.upper())
@@ -4025,7 +4040,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
 
     # hard link related tests
     def test_link_delete(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
 
         file1_path = self.make_path("test_file1")
         file2_path = self.make_path("test_file2")
@@ -4042,7 +4057,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
             self.assertEqual(f.read(), contents1)
 
     def test_link_is_existing_file(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_path = self.make_path("foo", "bar")
         self.create_file(file_path)
         self.assert_raises_os_error(
@@ -4051,7 +4066,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
 
     def test_link_is_broken_symlink(self):
         # Regression test for #311
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.check_case_insensitive_fs()
         file_path = self.make_path("baz")
         self.create_file(file_path)
@@ -4062,7 +4077,7 @@ class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
 
     def test_link_with_changed_case(self):
         # Regression test for #312
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.check_case_insensitive_fs()
         link_path = self.make_path("link")
         self.os.symlink(self.base_path, link_path)
@@ -4440,7 +4455,7 @@ class FakeOsModuleLowLevelFileOpTest(FakeOsModuleTestBase):
 
     def test_open_exclusive_if_symlink_exists_works_in_windows(self):
         self.check_windows_only()
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("link")
         link_target = self.make_path("link_target")
         self.os.symlink(link_target, link_path)
@@ -5385,7 +5400,7 @@ class FakeScandirTest(FakeOsModuleTestBase):
 
     def test_path_links_not_resolved(self):
         # regression test for #350
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         dir_path = self.make_path("A", "B", "C")
         self.os.makedirs(self.os.path.join(dir_path, "D"))
         link_path = self.make_path("A", "C")
diff --git a/pyfakefs/tests/fake_pathlib_test.py b/pyfakefs/tests/fake_pathlib_test.py
index fa8fedf..2e19048 100644
--- a/pyfakefs/tests/fake_pathlib_test.py
+++ b/pyfakefs/tests/fake_pathlib_test.py
@@ -39,7 +39,7 @@ from pyfakefs.tests.skipped_pathlib import (
     read_pathlib,
     read_text_pathlib,
 )
-from pyfakefs.tests.test_utils import RealFsTestMixin
+from pyfakefs.tests.test_utils import RealFsTestMixin, skip_if_symlink_not_supported
 
 is_windows = sys.platform == "win32"
 
@@ -478,7 +478,7 @@ class FakePathlibFileObjectPropertyTest(RealPathlibTestCase):
         self.create_file(self.file_path, contents=b"a" * 100)
         self.create_dir(self.make_path("home", "john"))
         try:
-            self.skip_if_symlink_not_supported()
+            skip_if_symlink_not_supported()
         except unittest.SkipTest:
             return
         self.create_symlink(self.make_path("john"), self.make_path("home", "john"))
@@ -496,7 +496,7 @@ class FakePathlibFileObjectPropertyTest(RealPathlibTestCase):
         self.os.umask(self.umask)
 
     def test_exists(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.assertTrue(self.path(self.file_path).exists())
         self.assertTrue(self.path(self.make_path("home", "jane")).exists())
         self.assertFalse(self.path(self.make_path("home", "jane", "test")).exists())
@@ -506,7 +506,7 @@ class FakePathlibFileObjectPropertyTest(RealPathlibTestCase):
         self.assertFalse(self.path(self.make_path("broken_file_link")).exists())
 
     def test_is_dir(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.assertFalse(self.path(self.file_path).is_dir())
         self.assertTrue(self.path(self.make_path("home/jane")).is_dir())
         self.assertTrue(self.path(self.make_path("john")).is_dir())
@@ -515,7 +515,7 @@ class FakePathlibFileObjectPropertyTest(RealPathlibTestCase):
         self.assertFalse(self.path(self.make_path("broken_file_link")).is_dir())
 
     def test_is_file(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.assertTrue(self.path(self.make_path("home/jane/test.py")).is_file())
         self.assertFalse(self.path(self.make_path("home/jane")).is_file())
         self.assertFalse(self.path(self.make_path("john")).is_file())
@@ -524,7 +524,7 @@ class FakePathlibFileObjectPropertyTest(RealPathlibTestCase):
         self.assertFalse(self.path(self.make_path("broken_file_link")).is_file())
 
     def test_is_symlink(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.assertFalse(self.path(self.make_path("home/jane/test.py")).is_symlink())
         self.assertFalse(self.path(self.make_path("home/jane")).is_symlink())
         self.assertTrue(self.path(self.make_path("john")).is_symlink())
@@ -533,7 +533,7 @@ class FakePathlibFileObjectPropertyTest(RealPathlibTestCase):
         self.assertTrue(self.path(self.make_path("broken_file_link")).is_symlink())
 
     def test_stat(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_stat = self.os.stat(self.file_path)
 
         stat_result = self.path(self.file_link_path).stat()
@@ -545,7 +545,7 @@ class FakePathlibFileObjectPropertyTest(RealPathlibTestCase):
         self.assertEqual(stat_result[stat.ST_MTIME], int(file_stat.st_mtime))
 
     def check_lstat(self, expected_size):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_stat = self.os.lstat(self.file_link_path)
 
         stat_result = self.path(self.file_link_path).lstat()
@@ -561,7 +561,7 @@ class FakePathlibFileObjectPropertyTest(RealPathlibTestCase):
 
     @unittest.skipIf(not is_windows, "Windows specific behavior")
     def test_lstat_windows(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.check_lstat(0)
 
     @unittest.skipIf(is_windows, "POSIX specific behavior")
@@ -575,7 +575,7 @@ class FakePathlibFileObjectPropertyTest(RealPathlibTestCase):
         self.assertEqual(link_stat.st_mode, stat.S_IFLNK | mode)
 
     def test_lchmod(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_stat = self.os.stat(self.file_path)
         link_stat = self.os.lstat(self.file_link_path)
         if not hasattr(self.real_os, "lchmod"):
@@ -594,7 +594,7 @@ class FakePathlibFileObjectPropertyTest(RealPathlibTestCase):
         "follow_symlinks argument new in Python 3.10",
     )
     def test_chmod_no_followsymlinks(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_stat = self.os.stat(self.file_path)
         link_stat = self.os.lstat(self.file_link_path)
         if self.os.chmod not in self.os.supports_follow_symlinks or IS_PYPY:
@@ -747,7 +747,7 @@ class FakePathlibPathFileOperationTest(RealPathlibTestCase):
     """Tests methods related to file and directory handling."""
 
     def test_exists(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         self.create_file(self.make_path("home", "jane", "test.py"))
         self.create_dir(self.make_path("home", "john"))
         self.create_symlink(self.make_path("john"), self.make_path("home", "john"))
@@ -889,7 +889,7 @@ class FakePathlibPathFileOperationTest(RealPathlibTestCase):
         )
 
     def test_symlink_to(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_name = self.make_path("foo", "bar.txt")
         self.create_file(file_name)
         link_name = self.make_path("link_to_bar")
@@ -901,7 +901,7 @@ class FakePathlibPathFileOperationTest(RealPathlibTestCase):
     @unittest.skipIf(sys.version_info < (3, 8), "link_to new in Python 3.8")
     @unittest.skipIf(sys.version_info >= (3, 12), "link_to removed in Python 3.12")
     def test_link_to(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_name = self.make_path("foo", "bar.txt")
         self.create_file(file_name)
         self.assertEqual(1, self.os.stat(file_name).st_nlink)
@@ -926,7 +926,7 @@ class FakePathlibPathFileOperationTest(RealPathlibTestCase):
 
     @unittest.skipIf(sys.version_info < (3, 9), "readlink new in Python 3.9")
     def test_readlink(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("foo", "bar", "baz")
         target = self.make_path("tarJAY")
         self.create_symlink(link_path, target)
@@ -1205,7 +1205,7 @@ class FakePathlibUsageInOsFunctionsTest(RealPathlibTestCase):
         self.os.chmod(self.path(path), 0o666)
 
     def test_link(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file1_path = self.make_path("test_file1")
         file2_path = self.make_path("test_file2")
         self.create_file(file1_path)
@@ -1240,7 +1240,7 @@ class FakePathlibUsageInOsFunctionsTest(RealPathlibTestCase):
         "under Windows before Python 3.8",
     )
     def test_readlink(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path("foo", "bar", "baz")
         target = self.make_path("tarJAY")
         self.create_symlink(link_path, target)
@@ -1252,7 +1252,7 @@ class FakePathlibUsageInOsFunctionsTest(RealPathlibTestCase):
         "under Windows before Python 3.8",
     )
     def test_readlink_bytes(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         link_path = self.make_path(b"foo", b"bar", b"baz")
         target = self.make_path(b"tarJAY")
         self.create_symlink(link_path, target)
@@ -1296,7 +1296,7 @@ class FakePathlibUsageInOsFunctionsTest(RealPathlibTestCase):
         self.assertEqual(1, len(dir_entries))
 
     def test_symlink(self):
-        self.skip_if_symlink_not_supported()
+        skip_if_symlink_not_supported()
         file_path = self.make_path("test_file1")
         link_path = self.make_path("link")
         self.create_file(file_path)
diff --git a/pyfakefs/tests/test_utils.py b/pyfakefs/tests/test_utils.py
index a5b35c6..113880e 100644
--- a/pyfakefs/tests/test_utils.py
+++ b/pyfakefs/tests/test_utils.py
@@ -67,7 +67,6 @@ class TestCase(unittest.TestCase):
     is_windows = sys.platform == "win32"
     is_cygwin = sys.platform == "cygwin"
     is_macos = sys.platform == "darwin"
-    symlinks_can_be_tested = None
 
     def assert_mode_equal(self, expected, actual):
         return self.assertEqual(stat.S_IMODE(expected), stat.S_IMODE(actual))
@@ -84,6 +83,13 @@ class TestCase(unittest.TestCase):
                 self.assertEqual(subtype, exc.errno)
 
 
+def skip_if_symlink_not_supported():
+    """If called at test start, tests are skipped if symlinks are not
+    supported."""
+    if TestCase.is_windows and not is_root():
+        raise unittest.SkipTest("Symlinks under Windows need admin privileges")
+
+
 class RealFsTestMixin:
     """Test mixin to allow tests to run both in the fake filesystem and in the
     real filesystem.
@@ -269,20 +275,6 @@ class RealFsTestMixin:
                     "Skipping because FakeFS does not match real FS"
                 )
 
-    def symlink_can_be_tested(self):
-        """Used to check if symlinks and hard links can be tested under
-        Windows. All tests are skipped under Windows for Python versions
-        not supporting links, and real tests are skipped if running without
-        administrator rights.
-        """
-        return not TestCase.is_windows or is_root()
-
-    def skip_if_symlink_not_supported(self):
-        """If called at test start, tests are skipped if symlinks are not
-        supported."""
-        if not self.symlink_can_be_tested():
-            raise unittest.SkipTest("Symlinks under Windows need admin privileges")
-
     def make_path(self, *args):
         """Create a path with the given component(s). A base path is prepended
         to the path which represents a temporary directory in the real FS,