diff --git a/lib/modules/manager/git-submodules/extract.spec.ts b/lib/modules/manager/git-submodules/extract.spec.ts
index 0f6df2f7737732d10460eb226fd4fa6dccdbe45d..6850f30f1930f47bd3cc04f231f349b0b3e67d1b 100644
--- a/lib/modules/manager/git-submodules/extract.spec.ts
+++ b/lib/modules/manager/git-submodules/extract.spec.ts
@@ -1,69 +1,83 @@
 import is from '@sindresorhus/is';
 import { mock } from 'jest-mock-extended';
-import _simpleGit, {
-  Response,
-  SimpleGit,
-  SimpleGitFactory,
-  TaskOptions,
-} from 'simple-git';
+import { Response, SimpleGit, SimpleGitFactory, simpleGit } from 'simple-git';
 import { GlobalConfig } from '../../../config/global';
 import * as hostRules from '../../../util/host-rules';
-import type { PackageFileContent } from '../types';
 import { extractPackageFile } from '.';
 
 jest.mock('simple-git');
-const simpleGit: jest.Mock<Partial<SimpleGit>> = _simpleGit as never;
+const simpleGitFactoryMock = simpleGit as jest.Mock<Partial<SimpleGit>>;
 // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
 const Git = jest.requireActual('simple-git') as SimpleGitFactory;
 
+const gitMock = mock<SimpleGit>();
+
 describe('modules/manager/git-submodules/extract', () => {
-  // flaky ci tests
-  // jest.setTimeout(10 * 1000);
+  beforeEach(() => {
+    GlobalConfig.set({ localDir: `${__dirname}/__fixtures__` });
+    // clear host rules
+    hostRules.clear();
+    // clear environment variables
+    process.env = {};
 
-  beforeAll(() => {
-    simpleGit.mockImplementation((basePath: string) => {
+    simpleGitFactoryMock.mockImplementation((basePath: string) => {
       const git = Git(basePath);
-      const lsRemote: Record<string, string> = {
-        'https://abc@domain.test/some/other.git': '',
-        'https://gitlab-ci-token:xyz@gitlab.com/some/repo.git':
-          'ref: refs/heads/dev  HEAD\n',
-      };
-      return {
-        subModule(): Response<string> {
-          return Promise.resolve(
-            '4b825dc642cb6eb9a060e54bf8d69288fbee4904'
-          ) as Response<string>;
-        },
-        raw(options: string | string[] | TaskOptions): Response<string> {
-          if (
-            (is.string(options) || is.array(options, is.string)) &&
-            options.includes('remote.origin.url')
-          ) {
-            return Promise.resolve(
-              'https://github.com/renovatebot/renovate.git'
-            ) as Response<string>;
-          }
-          return git.raw(options);
-        },
-        listRemote(options: TaskOptions): Response<string> {
-          if (
-            is.array(options, is.string) &&
-            lsRemote[options[1]] !== undefined
-          ) {
-            return Promise.resolve(lsRemote[options[1]]) as Response<string>;
-          }
+
+      gitMock.env.mockImplementation(() => gitMock);
+      gitMock.subModule.mockResolvedValue(
+        '4b825dc642cb6eb9a060e54bf8d69288fbee4904'
+      );
+
+      gitMock.raw.mockImplementation((options) => {
+        if (
+          (is.string(options) || is.array(options, is.string)) &&
+          options.includes('remote.origin.url')
+        ) {
           return Promise.resolve(
-            'ref: refs/heads/main  HEAD\n5701164b9f5edba1f6ca114c491a564ffb55a964        HEAD'
+            'https://github.com/renovatebot/renovate.git'
           ) as Response<string>;
-        },
-        ...mock<Omit<SimpleGit, 'subModule' | 'raw' | 'listRemote'>>(),
-      };
+        }
+        return git.raw(options);
+      });
+      return gitMock;
     });
   });
 
   describe('extractPackageFile()', () => {
-    it('extracts submodules', async () => {
-      GlobalConfig.set({ localDir: `${__dirname}/__fixtures__` });
+    it('empty submodule returns null', async () => {
+      expect(await extractPackageFile('', '.gitmodules.1', {})).toBeNull();
+    });
+
+    it('default branch is detected when no branch is specified', async () => {
+      gitMock.listRemote.mockResolvedValueOnce(
+        'ref: refs/heads/main  HEAD\n5701164b9f5edba1f6ca114c491a564ffb55a964        HEAD'
+      );
+      const res = await extractPackageFile('', '.gitmodules.2', {});
+      expect(res?.deps).toHaveLength(1);
+      expect(res?.deps[0].currentValue).toBe('main');
+    });
+
+    it('default to master if no branch can be detected', async () => {
+      const res = await extractPackageFile('', '.gitmodules.2', {});
+      expect(res?.deps).toHaveLength(1);
+      expect(res?.deps[0].currentValue).toBe('master');
+    });
+
+    it('given branch is used when branch is specified', async () => {
+      const res = await extractPackageFile('', '.gitmodules.3', {});
+      expect(res?.deps).toHaveLength(1);
+      expect(res?.deps[0].currentValue).toBe('staging');
+    });
+
+    it('submodule packageName is constructed from relative path', async () => {
+      const res = await extractPackageFile('', '.gitmodules.4', {});
+      expect(res?.deps).toHaveLength(1);
+      expect(res?.deps[0].packageName).toBe(
+        'https://github.com/PowerShell/PowerShell-Docs'
+      );
+    });
+
+    it('extracts multiple submodules', async () => {
       hostRules.add({ matchHost: 'github.com', token: '123test' });
       hostRules.add({
         matchHost: 'domain.test',
@@ -75,16 +89,22 @@ describe('modules/manager/git-submodules/extract', () => {
         token: 'xyz',
         hostType: 'gitlab',
       });
-      let res: PackageFileContent | null;
-      expect(await extractPackageFile('', '.gitmodules.1', {})).toBeNull();
-      res = await extractPackageFile('', '.gitmodules.2', {});
-      expect(res?.deps).toHaveLength(1);
-      expect(res?.deps[0].currentValue).toBe('main');
-      res = await extractPackageFile('', '.gitmodules.3', {});
-      expect(res?.deps).toHaveLength(1);
-      res = await extractPackageFile('', '.gitmodules.4', {});
-      expect(res?.deps).toHaveLength(1);
-      res = await extractPackageFile('', '.gitmodules.5', {});
+      gitMock.listRemote.mockResolvedValueOnce(
+        'ref: refs/heads/main  HEAD\n5701164b9f5edba1f6ca114c491a564ffb55a964        HEAD'
+      );
+      gitMock.listRemote.mockResolvedValueOnce(
+        'ref: refs/heads/main  HEAD\n5701164b9f5edba1f6ca114c491a564ffb55a964        HEAD'
+      );
+      gitMock.listRemote.mockResolvedValueOnce(
+        'ref: refs/heads/main  HEAD\n5701164b9f5edba1f6ca114c491a564ffb55a964        HEAD'
+      );
+      gitMock.listRemote.mockResolvedValueOnce(
+        'ref: refs/heads/master  HEAD\n5701164b9f5edba1f6ca114c491a564ffb55a964        HEAD'
+      );
+      gitMock.listRemote.mockResolvedValueOnce(
+        'ref: refs/heads/dev  HEAD\n5701164b9f5edba1f6ca114c491a564ffb55a964        HEAD'
+      );
+      const res = await extractPackageFile('', '.gitmodules.5', {});
       expect(res).toEqual({
         datasource: 'git-refs',
         deps: [
diff --git a/lib/modules/manager/git-submodules/update.spec.ts b/lib/modules/manager/git-submodules/update.spec.ts
index a9ef0700134f89bd504904914995be4de04c9ba2..6b5111ed5cea27cb4a6e099e1c989dbad7430fd2 100644
--- a/lib/modules/manager/git-submodules/update.spec.ts
+++ b/lib/modules/manager/git-submodules/update.spec.ts
@@ -1,15 +1,29 @@
-import _simpleGit, { Response, SimpleGit } from 'simple-git';
+import { mock } from 'jest-mock-extended';
+import { SimpleGit, simpleGit } from 'simple-git';
 import { DirectoryResult, dir } from 'tmp-promise';
 import { join } from 'upath';
 import { GlobalConfig } from '../../../config/global';
 import type { RepoGlobalConfig } from '../../../config/types';
+import * as hostRules from '../../../util/host-rules';
 import type { Upgrade } from '../types';
 import { updateDependency } from '.';
 
 jest.mock('simple-git');
-const simpleGit: jest.Mock<Partial<SimpleGit>> = _simpleGit as never;
+const simpleGitFactoryMock = simpleGit as jest.Mock<Partial<SimpleGit>>;
+const gitMock = mock<SimpleGit>();
 
 describe('modules/manager/git-submodules/update', () => {
+  beforeEach(() => {
+    GlobalConfig.set({ localDir: `${__dirname}/__fixtures__` });
+    // clear host rules
+    hostRules.clear();
+    // clear environment variables
+    process.env = {};
+
+    simpleGitFactoryMock.mockReturnValue(gitMock);
+    gitMock.env.mockImplementation(() => gitMock);
+  });
+
   describe('updateDependency', () => {
     let upgrade: Upgrade;
     let adminConfig: RepoGlobalConfig;
@@ -29,11 +43,8 @@ describe('modules/manager/git-submodules/update', () => {
     });
 
     it('returns null on error', async () => {
-      simpleGit.mockReturnValue({
-        submoduleUpdate() {
-          throw new Error();
-        },
-      });
+      gitMock.submoduleUpdate.mockRejectedValue(new Error());
+
       const update = await updateDependency({
         fileContent: '',
         upgrade,
@@ -42,14 +53,9 @@ describe('modules/manager/git-submodules/update', () => {
     });
 
     it('returns content on update', async () => {
-      simpleGit.mockReturnValue({
-        submoduleUpdate() {
-          return Promise.resolve('') as Response<string>;
-        },
-        checkout() {
-          return Promise.resolve('') as Response<string>;
-        },
-      });
+      gitMock.submoduleUpdate.mockResolvedValue('');
+      gitMock.checkout.mockResolvedValue('');
+
       const update = await updateDependency({
         fileContent: '',
         upgrade,