You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jest中Mock navigator.mediaDevices(Create React App环境)

Fixing Navigator.mediaDevices Mocking in Create React App Tests

Let's walk through why your current mocks aren't working and how to fix them properly.

Why Your Previous Approaches Failed

  1. Scope Mismatch: In Jest, browser globals like navigator live on the global object, not always window. Your mock targeting window.navigator might not have been applied to the correct scope.
  2. Read-Only Property: The default navigator object in jsdom is often read-only. Directly assigning global.navigator = navigatorMock won't work unless you explicitly mark it as writable.
  3. Import Order: If your getConnectedDevices function accesses navigator.mediaDevices when it's imported (not just when called), your mock needs to be applied before importing the function.

Solution 1: Global Mock in setupTests.ts

This is the cleanest approach for all tests. Update your src/setupTests.ts to properly define the writable mock:

import "@testing-library/jest-dom";

// Mock navigator.mediaDevices globally
Object.defineProperty(global.navigator, 'mediaDevices', {
  writable: true,
  configurable: true,
  value: {
    enumerateDevices: jest.fn().mockResolvedValue([
      // Optional: Add mock device data for realistic testing
      {
        deviceId: 'camera-1',
        kind: 'videoinput',
        label: 'Built-in Camera',
        groupId: 'group-1'
      },
      {
        deviceId: 'mic-1',
        kind: 'audioinput',
        label: 'Built-in Microphone',
        groupId: 'group-1'
      }
    ]),
  },
});

This ensures the mock is available before any test files or components are loaded.

Solution 2: Mock Directly in the Test File

If you need a test-specific mock, apply it before importing your function:

// First apply the mock
Object.defineProperty(global.navigator, 'mediaDevices', {
  writable: true,
  value: {
    enumerateDevices: jest.fn().mockResolvedValue([]),
  },
});

// Then import the function under test
import getConnectedDevices from "./getConnectedDevices";

describe("getConnectedDevices", () => {
  it("Should work with mocked mediaDevices", async () => {
    console.log(navigator.mediaDevices); // Will now be defined
    const devices = await getConnectedDevices();
    
    expect(devices).toEqual([]);
    expect(navigator.mediaDevices.enumerateDevices).toHaveBeenCalled();
  });
});

Solution 3: SpyOn for Targeted Method Mocking

If you only need to mock the enumerateDevices method (and keep the rest of mediaDevices intact):

import getConnectedDevices from "./getConnectedDevices";

describe("getConnectedDevices", () => {
  beforeEach(() => {
    // Ensure mediaDevices exists first
    if (!navigator.mediaDevices) {
      Object.defineProperty(navigator, 'mediaDevices', {
        writable: true,
        value: {},
      });
    }
    // Spy on and mock the method
    jest.spyOn(navigator.mediaDevices, 'enumerateDevices')
      .mockResolvedValue([/* your mock devices */]);
  });

  afterEach(() => {
    // Clean up mocks to avoid test pollution
    jest.restoreAllMocks();
  });

  it("Should fetch connected devices correctly", async () => {
    const devices = await getConnectedDevices();
    expect(navigator.mediaDevices.enumerateDevices).toHaveBeenCalledTimes(1);
    expect(devices).toHaveLength(2); // Match your mock data length
  });
});

Key Testing Tips

  • Handle Async Code: Always use async/await in your tests since enumerateDevices returns a promise.
  • Mock Realistic Data: Use device objects that match the structure your function expects (including kind, deviceId, etc.).
  • Clean Up Mocks: Use afterEach to restore mocks if using spyOn, so tests don't interfere with each other.

内容的提问来源于stack exchange,提问作者Karl Taylor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:43:51