Raina-软件测试指南
AI测试学习圈
面试题
关于作者
小红书
B站
AI测试学习圈
面试题
关于作者
小红书
B站
  • 面试题

    • 面试题导航
    • 01-测试基础理论面试题
    • 02-测试用例设计面试题
    • 03-功能测试面试题
    • 04-性能测试面试题
    • 05-自动化测试面试题
    • 06-接口测试面试题
    • 07-移动端测试面试题
    • 08-安全测试面试题
    • 09-测试管理面试题
    • 10-测试工具面试题
    • 11-敏捷测试面试题
    • 12-数据库测试面试题
    • 13-兼容性测试面试题
    • 14-测试环境管理面试题
    • 15-测试文档面试题
    • 16-测试编程语言面试题
    • 17-测试最佳实践面试题
    • 18-项目实战面试题
    • 19-AI测试基础与AI辅助测试面试题
    • 20-大模型LLM应用测试面试题
    • 21-AI Agent与AIGC评估面试题
    • 22-机器学习模型与AI安全测试面试题
    • 23-AI Agent协议与扩展机制测试面试题
    • 24-电商交易系统测试面试题
    • 25-IM即时通讯系统测试面试题
    • 26-支付与金融系统测试面试题

测试编程语言面试题

本文档共 75 道题:前 45 道按语言与框架主题编排,后 30 道为 Python / Java 的补充实战问答,题号从 Q46 起顺延,避免与主干题号重复。


一、Python测试编程(10题)

Q1: Python中常用的测试框架有哪些?

答案:

unittest 在标准库里,类与断言风格接近 JUnit,适合维护老项目或与标准库深度绑定的代码。pytest 是目前社区事实标准,自动发现用例、fixture、参数化和插件(覆盖率、HTML 报告、并行等)都很成熟。nose2 算 unittest 的扩展,强调发现规则与插件。doctest 把文档字符串里的示例当作用例执行,适合验证「文档即契约」的模块。新项目若无历史包袱,多数团队会直接选 pytest。

Q2: unittest框架如何使用?

答案:

用例写在继承 unittest.TestCase 的类里,方法名以 test_ 开头;setUp / tearDown 在每个方法前后跑,setUpClass / tearDownClass 在类级别跑一次。断言用 self.assertEqual 等,跳过用 @unittest.skip。入口一般是 unittest.main() 或由 IDE / CI 调用 discover。

import unittest

class TestCalculator(unittest.TestCase):
    
    def setUp(self):
        """测试前准备"""
        self.calc = Calculator()
    
    def tearDown(self):
        """测试后清理"""
        pass
    
    def test_add(self):
        """测试加法"""
        result = self.calc.add(2, 3)
        self.assertEqual(result, 5)
    
    def test_subtract(self):
        """测试减法"""
        result = self.calc.subtract(5, 3)
        self.assertEqual(result, 2)
    
    @unittest.skip("跳过测试")
    def test_skip(self):
        pass

if __name__ == '__main__':
    unittest.main()

Q3: pytest框架的特点是什么?

答案:

用例就是普通函数,断言直接写 assert,自带按路径与命名规则的用例发现。fixture 管准备数据与环境,生命周期可从函数级扩到 module/session;@pytest.mark.parametrize 做数据驱动,@pytest.mark 做冒烟、回归等筛选。生态里 pytest-cov、pytest-html、pytest-xdist 分别解决覆盖率、报告和并行,和 CI 对接也简单。

import pytest

def test_add():
    assert 2 + 3 == 5

@pytest.fixture
def calculator():
    return Calculator()

def test_calculator_add(calculator):
    assert calculator.add(2, 3) == 5

@pytest.mark.parametrize("a,b,expected", [
    (1, 2, 3),
    (2, 3, 5),
    (3, 4, 7),
])
def test_add_parametrize(a, b, expected):
    assert a + b == expected

Q4: pytest中如何使用Fixture?

答案:

@pytest.fixture 装饰的函数把「准备」写在 yield 之前,「清理」写在 yield 之后;测试函数把 fixture 名写在参数里即可注入。scope 控制复用范围(function、class、module、package、session),autouse=True 时不必显式传参也会执行。适合数据库连接、临时目录、登录态这类可复用的环境搭拆。

import pytest

@pytest.fixture
def database():
    """数据库Fixture"""
    db = Database()
    db.connect()
    yield db
    db.disconnect()

@pytest.fixture(scope="module")
def shared_resource():
    """模块级Fixture"""
    resource = Resource()
    yield resource
    resource.cleanup()

@pytest.fixture(autouse=True)
def setup():
    """自动使用的Fixture"""
    print("Setup")
    yield
    print("Teardown")

def test_example(database):
    """使用Fixture"""
    result = database.query("SELECT * FROM users")
    assert len(result) > 0

Q5: pytest中如何进行参数化测试?

答案:

@pytest.mark.parametrize 把多组输入绑到同一个测试函数上,失败时 pytest 会标出是哪一组数据出问题。多个 parametrize 叠在一起时会做笛卡尔积,用例数会相乘,适合组合较少时穷举边界;组合爆炸时更适合抽成显式的数据表或单独拆用例。

import pytest

@pytest.mark.parametrize("input,expected", [
    ("admin", True),
    ("user", False),
    ("guest", False),
])
def test_is_admin(input, expected):
    assert is_admin(input) == expected

@pytest.mark.parametrize("a", [1, 2, 3])
@pytest.mark.parametrize("b", [4, 5, 6])
def test_multiply(a, b):
    assert multiply(a, b) == a * b

Q6: Python中如何进行Mock测试?

答案:

unittest.mock 里的 Mock / MagicMock 造假对象,patch 在测试期间把目标路径上的符号替换成 mock,退出 with 或测试结束后自动还原。典型用法是挡住 HTTP、数据库、时间等外部依赖,只验证被测单元在「假定外部返回值」下的行为;return_value、side_effect 控制返回值与异常,assert_called_with 做调用校验。

from unittest.mock import Mock, patch, MagicMock

# 基本Mock
mock_obj = Mock()
mock_obj.method.return_value = 42
assert mock_obj.method() == 42

# 使用patch
@patch('module.function')
def test_with_patch(mock_function):
    mock_function.return_value = 'mocked'
    result = module.function()
    assert result == 'mocked'

# MagicMock
mock_obj = MagicMock()
mock_obj.attribute.method.return_value = 'value'
assert mock_obj.attribute.method() == 'value'

Q7: Python中如何测试异步代码?

答案:

异步协程用 pytest-asyncio 等插件,测试函数写成 async def 并打上 @pytest.mark.asyncio,内部 await 被测逻辑;event loop 可由 fixture 提供或在插件里托管。若被测代码混用同步封装,也可用 asyncio.run 包一层,但单测里更推荐统一 async 风格,避免在测试线程里反复起停 loop 埋雷。

import pytest
import asyncio

@pytest.mark.asyncio
async def test_async_function():
    result = await async_function()
    assert result == expected

# 使用pytest-asyncio
@pytest.fixture
def event_loop():
    loop = asyncio.get_event_loop()
    yield loop
    loop.close()

Q8: Python中如何生成测试报告?

答案:

pytest-html 生成可打开的 HTML;pytest-cov 出终端或 HTML 覆盖率;Allure 走「先落盘结果目录再 allure serve」的流程,适合和流水线、历史趋势对接。CI 里通常还要 JUnit XML(--junitxml)方便门禁汇总。

# pytest-html
pytest --html=report.html

# pytest-cov
pytest --cov=src --cov-report=html

# Allure
pytest --alluredir=allure-results
allure serve allure-results

Q9: Python中如何进行数据驱动测试?

答案:

除 parametrize 外,可从 CSV、Excel、JSON 读入行记录,在 fixture 里加载成列表再交给 parametrize;或循环里动态 pytest.param 注册。关键是数据与断言分离、失败时能定位到「第几行输入」,并注意大表的执行时间与筛选策略。

import pytest
import csv

def load_test_data():
    with open('test_data.csv') as f:
        reader = csv.DictReader(f)
        return list(reader)

@pytest.mark.parametrize("data", load_test_data())
def test_with_data(data):
    assert process_data(data['input']) == data['expected']

Q10: Python测试的最佳实践有哪些?

答案:

目录与命名跟框架约定走(如 test_*.py、Test* 类),保证单条用例可单独跑、不依赖执行顺序。准备与清理放进 fixture,测试数据尽量外置并可在失败后复现。断言写清「期望与实际」的关系,复杂逻辑拆成多条小断言或辅助函数,避免一条 assert 里夹太多布尔表达式,排错时才看得懂。


二、Java测试编程(5题)

Q11: Java中常用的测试框架有哪些?

答案:

JUnit 5(Jupiter)是当前主流,注解与扩展模型比 JUnit 4 更清晰。TestNG 在分组、依赖顺序、XML 套件和 DataProvider 上更灵活,老项目里常见。Mockito 负责打桩与校验调用,常和 JUnit 一起用。AssertJ 提供流式断言,链式写预期比一长串 assertEquals 更易读。REST 层还有 RestAssured 等,但单测语境下「JUnit + Mockito + AssertJ」出现频率最高。

Q12: JUnit 5的主要特性有哪些?

答案:

Jupiter 里生命周期用 @BeforeEach / @AfterEach、@BeforeAll / @AfterAll(静态方法时注意类级只跑一次)。@ParameterizedTest 搭配各种 Source 或 MethodSource 做数据驱动,@DisplayName 改善报告可读性。断言在 org.junit.jupiter.api.Assertions,常用 assertEquals、assertThrows 等;扩展模型允许自定义参数解析、模板方法等,比 JUnit 4 的 Runner 更统一。

import org.junit.jupiter.api.*;

class CalculatorTest {
    
    @BeforeAll
    static void setUpAll() {
        // 所有测试前执行一次
    }
    
    @BeforeEach
    void setUp() {
        // 每个测试前执行
    }
    
    @Test
    @DisplayName("测试加法")
    void testAdd() {
        Calculator calc = new Calculator();
        assertEquals(5, calc.add(2, 3));
    }
    
    @ParameterizedTest
    @ValueSource(ints = {1, 2, 3})
    void testParameterized(int value) {
        assertTrue(value > 0);
    }
}

Q13: TestNG的主要特性有哪些?

答案:

注解风格与 JUnit 类似但命名是 @BeforeMethod 等;@DataProvider 把二维数组或迭代器喂给多个 @Test。groups 把用例划成冒烟、回归等套件,在 testng.xml 里按组筛选执行,适合大而长的回归矩阵。依赖顺序、并行套件、监听器也是面试常提的差异化点。

@Test(groups = "smoke")
public void smokeTest() {
    // 冒烟测试
}

@Test(groups = "regression")
public void regressionTest() {
    // 回归测试
}

@DataProvider(name = "testData")
public Object[][] testData() {
    return new Object[][] {
        {1, 2, 3},
        {2, 3, 5},
        {3, 4, 7}
    };
}

@Test(dataProvider = "testData")
public void testAdd(int a, int b, int expected) {
    assertEquals(expected, a + b);
}

Q14: 如何使用Mockito进行Mock测试?

答案:

mock(Class) 生成替身,when(...).thenReturn / thenThrow 定行为,verify 校验是否调用及次数。@Mock + @InjectMocks 在单测类里自动注入依赖,减少手写 mock()。打桩时要 mock「被测类直接依赖的边界」,避免把整个调用树都 mock 掉导致测不到真实协作。

import static org.mockito.Mockito.*;

// 创建Mock对象
List<String> mockList = mock(List.class);

// 设置行为
when(mockList.get(0)).thenReturn("first");
when(mockList.get(1)).thenThrow(new RuntimeException());

// 验证调用
verify(mockList).get(0);
verify(mockList, times(2)).get(0);

// 使用@Mock注解
@Mock
private UserService userService;

@InjectMocks
private UserController userController;

Q15: Java中如何测试异常?

答案:

JUnit 5 用 assertThrows 包住会抛错的调用,并可对返回的异常继续做 message 断言。TestNG 仍常见 @Test(expectedExceptions = ...) 写法,缺点是无法在方法体中间精细断言异常属性,复杂场景更推荐 try/catch 或 assertThrows 风格。

// JUnit 5
@Test
void testException() {
    assertThrows(IllegalArgumentException.class, () -> {
        methodThatThrowsException();
    });
}

// TestNG
@Test(expectedExceptions = IllegalArgumentException.class)
public void testException() {
    methodThatThrowsException();
}

三、JavaScript测试编程(10题)

Q16: JavaScript中常用的测试框架有哪些?

答案:

Jest 自带断言、Mock、快照和用例发现,React 生态里出场率很高。Mocha 只做「跑测试」这一层,断言常配 Chai,灵活但配置项多。Jasmine 是 BDD 风格一体化方案,在浏览器端老项目里能见到。Vitest 与 Vite 同构,冷启动和 watch 很快,适合现代前端工程。选型上先看构建链(Webpack / Vite)和团队习惯,再决定要不要「全家桶」。

Q17: Jest的主要特性有哪些?

答案:

默认按约定发现 *.test.js / __tests__,内置 expect 断言与 mock 工厂,jest.fn()、jest.spyOn 改行为并统计调用。快照适合 UI 或大块 JSON 的回归对比;watch 模式在本地改代码时反馈快。与 Babel / TS 的 preset 配合即可,复杂 monorepo 再拆 jest.config。

// 基本测试
describe('Calculator', () => {
  test('adds 1 + 2 to equal 3', () => {
    expect(1 + 2).toBe(3);
  });
  
  test('object assignment', () => {
    const data = {one: 1};
    data['two'] = 2;
    expect(data).toEqual({one: 1, two: 2});
  });
});

// Mock
jest.mock('./api');
test('mocks API call', () => {
  const api = require('./api');
  api.getData.mockReturnValue({data: 'test'});
  expect(api.getData()).toEqual({data: 'test'});
});

Q18: Mocha如何使用?

答案:

describe / it 组织层级,before / after 与 beforeEach / afterEach 控制生命周期;断言需自行引入 assert 或 Chai。适合想精细控制 reporter、并行、文件 glob 的团队;代价是「默认能力」比 Jest 少,要自己拼测试栈。

const assert = require('assert');
const { describe, it, before, after, beforeEach, afterEach } = require('mocha');

describe('Calculator', () => {
  let calculator;
  
  before(() => {
    // 所有测试前执行一次
    calculator = new Calculator();
  });
  
  beforeEach(() => {
    // 每个测试前执行
    calculator.reset();
  });
  
  it('should add two numbers', () => {
    assert.equal(calculator.add(2, 3), 5);
  });
  
  it('should subtract two numbers', () => {
    assert.equal(calculator.subtract(5, 3), 2);
  });
  
  afterEach(() => {
    // 每个测试后执行
  });
  
  after(() => {
    // 所有测试后执行一次
  });
});

Q19: JavaScript中如何进行异步测试?

答案:

用例写成 async,内部 await Promise,或返回 Promise 让 Mocha 等待 settled。不要用裸 setTimeout 猜时间,尽量 await 业务 Promise;定时器场景配合 Jest fake timers。断言库要支持 Promise rejection(如 rejects 匹配器),否则容易「误绿」。

// Jest
test('async test', async () => {
  const result = await asyncFunction();
  expect(result).toBe('expected');
});

// Mocha
it('async test', async () => {
  const result = await asyncFunction();
  assert.equal(result, 'expected');
});

// 使用Promise
it('promise test', () => {
  return promiseFunction().then(result => {
    assert.equal(result, 'expected');
  });
});

Q20: JavaScript中如何进行Mock测试?

答案:

Jest 里 jest.mock hoist 到文件顶部,适合替换整个模块;函数级用 jest.fn()。Sinon 的 stub/spy 与 Mocha 常一起用,记得在 afterEach 里 restore,避免用例间泄漏。原则是只 mock IO 边界(网络、存储、第三方 SDK),不要 mock 正在验证的业务规则本身。

// Jest Mock
jest.mock('./api');
const api = require('./api');
api.getData = jest.fn().mockReturnValue({data: 'test'});

// Sinon Mock
const sinon = require('sinon');
const stub = sinon.stub(api, 'getData').returns({data: 'test'});

// 恢复Mock
stub.restore();

Q21: JavaScript中如何测试React组件?

答案:

Testing Library 主张从用户可见的 DOM 与 role 查询,而不是测组件内部 state 实现细节。render 后 screen.getByRole / getByText,交互走 fireEvent 或 user-event。这样重构内部实现时用例更稳,和「测行为不测结构」一致。

import { render, screen, fireEvent } from '@testing-library/react';
import Component from './Component';

test('renders component', () => {
  render(<Component />);
  const element = screen.getByText('Hello');
  expect(element).toBeInTheDocument();
});

test('handles click', () => {
  const handleClick = jest.fn();
  render(<Component onClick={handleClick} />);
  fireEvent.click(screen.getByRole('button'));
  expect(handleClick).toHaveBeenCalledTimes(1);
});

Q22: JavaScript中如何测试Vue组件?

答案:

@vue/test-utils 的 mount 得到 wrapper,可 find 元素、trigger 事件,必要时 nextTick 等 DOM 更新。Vue 3 组合式 API 下优先断言暴露给模板的接口(props、emit、插槽内容),少直接钻 wrapper.vm 私有字段,降低与实现绑死。

import { mount } from '@vue/test-utils';
import Component from './Component.vue';

test('renders component', () => {
  const wrapper = mount(Component);
  expect(wrapper.text()).toContain('Hello');
});

test('handles click', async () => {
  const wrapper = mount(Component);
  await wrapper.find('button').trigger('click');
  expect(wrapper.vm.count).toBe(1);
});

Q23: JavaScript中如何测试API调用?

答案:

在单测里不要打真实外网,可用 fetch-mock、axios-mock-adapter、MSW 等在进程内拦截请求并返回固定 JSON。MSW 还能在浏览器层统一拦截,Storybook 与单测共用同一套 handler,减少「mock 写法两套」的维护成本。

// 使用fetch-mock
import fetchMock from 'fetch-mock';

test('API call', async () => {
  fetchMock.get('/api/users', {users: []});
  const response = await fetch('/api/users');
  const data = await response.json();
  expect(data.users).toEqual([]);
  fetchMock.restore();
});

// 使用axios-mock-adapter
import MockAdapter from 'axios-mock-adapter';
const mock = new MockAdapter(axios);
mock.onGet('/api/users').reply(200, {users: []});

Q24: JavaScript中如何测试定时器?

答案:

Jest 下 jest.useFakeTimers() 后可用 advanceTimersByTime 推进时间,避免测试里真 sleep。测完 useRealTimers 还原,防止影响其他用例。Mocha 里可借助 Sinon fake timers 同类能力。

// Jest
jest.useFakeTimers();

test('timer test', () => {
  const callback = jest.fn();
  setTimeout(callback, 1000);
  
  jest.advanceTimersByTime(1000);
  expect(callback).toHaveBeenCalled();
  
  jest.useRealTimers();
});

Q25: JavaScript测试的最佳实践有哪些?

答案:

文件命名用 *.test / *.spec 与仓库约定一致,describe 文案写清业务场景。每条用例独立,共享逻辑放 beforeEach,避免隐式依赖执行顺序。Mock 只挡外部,断言信息要能直接看出「谁错了、期望什么」。异步与定时器用框架提供的 fake / await,不用 setTimeout 硬等。


四、测试框架使用(10题)

Q26: 如何选择合适的测试框架?

答案:

先分清测的是 UI、接口还是纯逻辑:浏览器 E2E 常见 Playwright / Cypress,移动端 Appium,Java 服务层 JUnit + RestAssured 等。语言栈与构建工具要匹配(如 Vite 项目优先考虑 Vitest)。再看团队熟练度、社区与插件、CI 里是否好出 JUnit XML / 覆盖率。没有银弹,往往是「单测框架 + 专用 E2E / 接口工具」组合。

Q27: 测试框架的核心组件有哪些?

答案:

一套完整方案通常包括:运行器(发现与调度用例)、断言库(表达期望与失败信息)、Mock/Stub(隔离外部)、fixture 或 rule(准备数据与环境)、报告与覆盖率收集。不同语言里这些能力可能打在一个包(Jest)或多个包(Mocha + Chai + Sinon)里,面试时能说清职责边界即可。

Q28: 如何配置测试框架?

答案:

pytest 用 pytest.ini / pyproject.toml 配 testpaths、addopts、标记注册等;Jest 用 jest.config.js 配 testMatch、setupFilesAfterEnv、覆盖率收集范围。TestNG 常用 testng.xml 描述套件与分组。配置检入版本库,本地与 CI 共用同一份,避免「我机器能过」的漂移。

# pytest.ini
[pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts = -v --html=report.html
// jest.config.js
module.exports = {
  testMatch: ['**/__tests__/**/*.js'],
  coverageDirectory: 'coverage',
  collectCoverageFrom: ['src/**/*.js'],
};

Q29: 如何扩展测试框架?

答案:

pytest 可写 conftest.py 与 entry point 插件,注册自定义 fixture、命令行选项、报告钩子。Jest 用 setupFiles、自定义 testEnvironment、reporter。JUnit 5 用 Extension 模型拦截生命周期。扩展时尽量保持「业务项目依赖少」,把通用逻辑做成独立包或团队内插件,避免每个仓库复制粘贴一大段 glue code。

Q30: 如何集成测试框架到CI/CD?

答案:

流水线里用与本地相同的安装与命令(如 pytest、npm test),失败时非零退出码让门禁变红。产出 JUnit XML、覆盖率 Cobertura / lcov 上传,PR 上才能展示趋势。大仓库可拆并行 job 或按标记分阶段跑,先快后慢。

pipeline {
    agent any
    stages {
        stage('Test') {
            steps {
                sh 'pytest --junitxml=results.xml'
            }
        }
    }
    post {
        always {
            junit 'results.xml'
        }
    }
}
test:
  script:
    - pytest --cov=src --cov-report=xml
  coverage: '/TOTAL.*\s+(\d+%)$/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml
- name: Run tests
  run: pytest
  
- name: Upload coverage
  uses: codecov/codecov-action@v2

Q31: 测试框架的性能优化有哪些?

答案:

能并行就并行(pytest-xdist、Jest workers、TestNG parallel),长耗时用例打标记做夜间全量、提交时增量。fixture 提高 scope 减少重复建连,容器类集成测可复用一次环境。缓存要谨慎,避免把脏状态缓存进「绿」结果。

Q32: 如何管理测试数据?

答案:

生成侧可用固定表、工厂函数或 faker;存储可放文件、内嵌 DB、容器里的真实库。关键是隔离:每条用例用自己的主键空间或事务回滚,跑完可重复。不要在共享环境里假设「上次留下的数据还在」。

Q33: 如何实现测试报告?

答案:

终端适合开发反馈,HTML / Allure 适合人类浏览历史,XML / JSON 给流水线解析。报告里除了 pass/fail,还应能跳转到日志与截图(UI 失败时)。与通知(邮件、IM、PR comment)联动时,摘要里要带构建号与分支。

Q34: 如何实现测试标记和过滤?

答案:

pytest 的 @pytest.mark 与 -m 过滤,TestNG 的 groups,Jest 的 testPathPattern / 项目内自定义 tag。标记语义要在团队文档里统一(如 smoke、integration),否则每个人一套名字,CI 筛选会失控。

# pytest
@pytest.mark.smoke
def test_smoke():
    pass

@pytest.mark.regression
def test_regression():
    pass

# 执行标记测试
pytest -m smoke
// TestNG
@Test(groups = "smoke")
public void smokeTest() {
}

// 执行分组测试
@Test(groups = {"smoke", "regression"})

Q35: 测试框架的最佳实践有哪些?

答案:

框架与版本在团队层面统一,目录约定写进 README。用例分层:单测快而多、集成测少而关键、E2E 最省着用。定期删无效用例、给慢用例做 profiling,避免「全绿但没人敢跑全套」。


五、测试库和工具(10题)

Q36: Python中常用的测试库有哪些?

答案:

requests-mock、responses 拦截 HTTP,适合测调用第三方 API 的客户端。freezegun 冻结时间,测定时任务与过期逻辑。faker 造姓名地址等伪数据。hypothesis 做基于属性的随机探索,能挖出边界组合。按「是否真发网络、是否要真实时间流逝」选型即可。

Q37: Java中常用的测试库有哪些?

答案:

RestAssured 用 DSL 描述 HTTP 期望;WireMock 起本地桩服务;Awaitility 轮询异步条件直到超时;TestContainers 拉起真实数据库、消息队列做集成测。AssertJ 与 Mockito、JUnit 常一起出现,负责把断言写成人话。

Q38: JavaScript中常用的测试库有哪些?

答案:

@testing-library/react、vue 侧重用户视角渲染与交互。Sinon 提供 spy/stub/mock。nock 在 Node 里挡 HTTP。MSW 可在浏览器与 Node 共用 handler,适合「同一套假接口」贯穿单测与本地开发。

Q39: 如何选择测试库?

答案:

对照需求表:是否覆盖你的协议(HTTP、gRPC、WS)、是否易与当前测试框架集成、维护活跃度与 issue 响应、许可证与体积。小团队优先选「一个问题一个主流库」,避免同一层叠三套 mock 方案。

Q40: 如何集成测试工具?

答案:

IDE 里跑单测、断点调试;Maven / Gradle / npm scripts 统一入口;CI 里同一命令无头执行。监控侧可把失败用例与 trace id 关联,方便对照线上。核心是「本地 = CI」命令一致。

Q41: 如何管理测试依赖?

答案:

声明在 requirements.txt、package.json、pom.xml 等,生产依赖与 test 依赖分离(scope / devDependencies)。版本锁定或锁文件避免漂移,升级前在分支跑全套。容器或 venv 保证隔离。

Q42: 如何实现测试工具链?

答案:

从开发者提交到合并主线:格式化与静态检查 → 单测 → 构建镜像 → 集成/E2E → 报告归档。工具链上每一步失败都能快速定位日志与产物,比堆工具数量更重要。

Q43: 测试库的版本管理策略是什么?

答案:

主版本升级前读 changelog,在分支跑全量并关注破坏性变更。锁版本减少「上周还能过」的惊喜;安全补丁可单独升小版本。多服务仓库可用矩阵 CI 验证兼容性。

Q44: 如何评估测试工具的质量?

答案:

看文档是否覆盖常见坑、默认错误信息是否可读、扩展点是否稳定。试用阶段用真实场景写一小撮用例,量一下学习成本与执行时间,再决定是否推广。

Q45: 测试工具的最佳实践有哪些?

答案:

工具少而精,能复用 handler、fixture、脚手架就抽公共库。升级有记录,废弃路径提前在团队频道同步。别为了覆盖率数字硬造无断言用例。


补充:Python测试编程(Q46–Q65)

Q46: Python中如何测试数据库操作?

答案:

单测可用 SQLite 内存库或 pytest 插件托管的临时库;集成测用 Testcontainers 或迁移到与线上一致的镜像。每个用例独立事务或 truncate 子集表,避免互相污染。断言重点放在「写入后读回」与约束(唯一键、外键)是否符合预期。

import pytest
from unittest.mock import Mock, patch

@pytest.fixture
def db_connection():
    conn = create_connection()
    yield conn
    conn.close()

def test_query(db_connection):
    result = db_connection.query("SELECT * FROM users")
    assert len(result) > 0

# 使用Mock
@patch('module.database.execute')
def test_insert(mock_execute):
    insert_user('test')
    mock_execute.assert_called_once()

Q47: Python中如何测试文件操作?

答案:

用 tempfile 或 pytest 的 tmp_path 造独立文件,读写后断言内容,在 finally 或 fixture 清理阶段删除,避免往仓库里写死路径。需要测编码、大文件时分段读,别把整个文件假设能一次载入内存。

import pytest
import tempfile
import os

def test_file_read():
    with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
        f.write('test content')
        temp_path = f.name
    
    try:
        with open(temp_path, 'r') as f:
            content = f.read()
        assert content == 'test content'
    finally:
        os.unlink(temp_path)

Q48: Python中如何测试多线程代码?

答案:

并发单测里先明确测的是「互斥是否生效」还是「无死锁」:前者用多线程压同一临界区,断言不变量;后者可设超时 join。pytest 里注意 fixture 是否线程安全,必要时缩小共享状态或改用进程级隔离。

import pytest
import threading
import time

def test_thread_safety():
    results = []
    lock = threading.Lock()
    
    def worker():
        with lock:
            results.append(1)
    
    threads = [threading.Thread(target=worker) for _ in range(10)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()
    
    assert len(results) == 10

Q49: Python中如何测试日志输出?

答案:

pytest 的 caplog 抓 handler 输出,适合做「是否打了某条告警」类断言;若只关心「调用了 logger 某方法」,可对 logger 做 patch。注意日志级别与 formatter 是否在测试配置里与线上一致,否则断言字符串会对不上。

import pytest
import logging
from unittest.mock import patch

def test_logging(caplog):
    logging.info('test message')
    assert 'test message' in caplog.text

# 使用Mock
@patch('module.logger.info')
def test_log_call(mock_log):
    function_that_logs()
    mock_log.assert_called_once()

Q50: Python中如何测试命令行工具?

答案:

Click 用 CliRunner.invoke,argparse 类可直接调 main(argv) 或封装成函数测退出码与 stdout。断言 stderr/exit code 与正常路径分开写,避免把 shell 真管道引进单测。

import pytest
from click.testing import CliRunner

def test_cli_command():
    runner = CliRunner()
    result = runner.invoke(cli, ['command', 'arg'])
    assert result.exit_code == 0
    assert 'expected output' in result.output

Q51: Python中如何测试Web应用?

答案:

Flask/Django 等都提供 test client,在 TESTING=True 下跑内存里的 WSGI,无需起真实端口。fixture 里创建 app 与 client,每个用例拿干净上下文;需要会话或 cookie 时按框架文档打开对应配置。

import pytest
from flask import Flask
from app import create_app

@pytest.fixture
def client():
    app = create_app()
    app.config['TESTING'] = True
    with app.test_client() as client:
        yield client

def test_homepage(client):
    response = client.get('/')
    assert response.status_code == 200

Q52: Python中如何测试API?

答案:

单测里 mock requests 或 httpx 的底层发送,只断言 URL、header、body 拼装是否正确;集成测再对真实服务或 WireMock 容器打 HTTP。别把「网络偶发超时」混进逻辑断言里。

import pytest
import requests
from unittest.mock import patch, Mock

@patch('requests.get')
def test_api_call(mock_get):
    mock_response = Mock()
    mock_response.json.return_value = {'data': 'test'}
    mock_get.return_value = mock_response
    
    response = requests.get('http://api.example.com/data')
    assert response.json() == {'data': 'test'}

Q53: Python中如何测试缓存?

答案:

对缓存 client 做 mock,或每个用例前 clear 再写入,断言命中与未命中两条路径。TTL 与淘汰策略可配合 freezegun 推进时间,避免 sleep。

import pytest
from unittest.mock import patch

@patch('module.cache.get')
def test_cache_hit(mock_get):
    mock_get.return_value = 'cached_value'
    result = get_cached_data('key')
    assert result == 'cached_value'

def test_cache_miss():
    cache.clear()
    result = get_cached_data('key')
    assert result is None

Q54: Python中如何测试配置管理?

答案:

用 patch.dict 改环境变量,或给配置模块注入临时 .env / dict,测不同分支。不要把生产密钥写进仓库,测试值走 fixture。

import pytest
import os
from unittest.mock import patch

@patch.dict(os.environ, {'CONFIG_KEY': 'test_value'})
def test_config_from_env():
    value = os.environ.get('CONFIG_KEY')
    assert value == 'test_value'

Q55: Python中如何测试加密解密?

答案:

用已知 key 与明文走 round-trip,断言解密结果一致;边界测空串、错误 key、篡改密文应抛错。随机 IV/salt 的场景只断言格式与长度,不硬编码整段密文。

import pytest
from cryptography.fernet import Fernet

def test_encryption():
    key = Fernet.generate_key()
    f = Fernet(key)
    message = b"secret message"
    encrypted = f.encrypt(message)
    decrypted = f.decrypt(encrypted)
    assert decrypted == message

Q56: Python中如何测试日期时间?

答案:

freezegun 把 datetime.now() 等钉在固定时刻,适合测「跨日结算」「定时任务 cron」分支。时区相关用 zoneinfo 或 pytz 显式构造 aware datetime,别依赖本机系统时区。

import pytest
from freezegun import freeze_time
from datetime import datetime

@freeze_time("2023-01-01")
def test_date():
    now = datetime.now()
    assert now.year == 2023
    assert now.month == 1
    assert now.day == 1

Q57: Python中如何测试随机数?

答案:

对 random / secrets 的调用点 patch,返回固定值,断言下游分支;若测统计性质,用固定 seed 的 Random 实例而不是全局 random.seed,避免用例间互相污染。

import pytest
from unittest.mock import patch
import random

@patch('random.randint')
def test_random(mock_randint):
    mock_randint.return_value = 42
    result = random.randint(1, 100)
    assert result == 42

Q58: Python中如何测试网络请求?

答案:

responses、requests-mock 等在进程内注册 URL 与响应体,不访问外网。断言 status、JSON 与异常路径(超时、5xx)时,把 retry 逻辑和「单次请求」拆开测。

import pytest
import responses

@responses.activate
def test_api():
    responses.add(
        responses.GET,
        'http://api.example.com/data',
        json={'data': 'test'},
        status=200
    )
    response = requests.get('http://api.example.com/data')
    assert response.json() == {'data': 'test'}

Q59: Python中如何测试异常处理?

答案:

pytest.raises 拿到异常对象后可继续断言 args 与 __cause__;match= 用正则收紧 message。业务码里若吞异常,单测要显式断言「不该静默」。

import pytest

def test_exception():
    with pytest.raises(ValueError) as exc_info:
        raise ValueError("test error")
    assert str(exc_info.value) == "test error"

def test_exception_message():
    with pytest.raises(ValueError, match="test"):
        raise ValueError("test error")

Q60: Python中如何测试上下文管理器?

答案:

with 块内断言资源已打开,离开块后断言已关闭或已提交。若实现 __exit__ 里吞异常,要单独用 pytest.raises 验证异常传播是否符合契约。

import pytest

def test_context_manager():
    with MyContext() as ctx:
        assert ctx.is_open
    assert ctx.is_closed

Q61: Python中如何测试装饰器?

答案:

在被装饰函数上取样返回值或元数据(如 __wrapped__),确认装饰器是否包装了原函数。避免只测装饰器内部 helper 而不测「对用户函数的可见效果」。

import pytest

def test_decorator():
    @my_decorator
    def function():
        return "result"
    
    result = function()
    assert result == "decorated result"

Q62: Python中如何测试生成器?

答案:

逐步 next 比对序列,耗尽时用 pytest.raises(StopIteration)。若生成器带 close,记得测 generator.close 的清理副作用。

import pytest

def test_generator():
    gen = my_generator()
    assert next(gen) == 1
    assert next(gen) == 2
    assert next(gen) == 3
    
    with pytest.raises(StopIteration):
        next(gen)

Q63: Python中如何测试类方法?

答案:

实例方法走对象状态,静态方法与类方法走类本身,别混测。下面把实例、静态、类方法拆成三个用例名,避免 pytest 收集时后者覆盖前者。

import pytest

def test_instance_method():
    result = obj.instance_method()
    assert result == "expected"

def test_static_method():
    result = MyClass.static_method()
    assert result == "expected"

def test_class_method_binding():
    result = MyClass.class_method()
    assert result == "expected"

Q64: Python中如何测试继承?

答案:

isinstance 与对父类行为的覆盖是否仍符合里氏替换;子类专有方法单独断言。抽象基类可测「未实现应无法实例化」。

import pytest

def test_inheritance():
    child = ChildClass()
    assert isinstance(child, ParentClass)
    assert child.parent_method() == "parent"
    assert child.child_method() == "child"

Q65: Python中如何测试多态?

答案:

同一接口上挂多种实现,循环里只调协议方法,断言返回值满足共同契约(类型、不变量)。别测具体子类分支细节,否则失去「多态」抽象的意义。

import pytest

def test_polymorphism():
    objects = [ClassA(), ClassB(), ClassC()]
    results = [obj.method() for obj in objects]
    assert all(isinstance(r, str) for r in results)

补充:Java测试编程(Q66–Q75)

Q66: Java中如何测试Spring应用?

答案:

@SpringBootTest 起完整上下文,@AutoConfigureMockMvc 测 MVC 层而不起真实端口;与 @WebMvcTest 相比前者更重,按是否加载全容器选型。断言 JSON 时用 jsonPath,避免手写字符串 contains。

@SpringBootTest
@AutoConfigureMockMvc
class SpringTest {
    @Autowired
    private MockMvc mockMvc;
    
    @Test
    void testEndpoint() throws Exception {
        mockMvc.perform(get("/api/users"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.length()").value(10));
    }
}

Q67: Java中如何测试数据库操作?

答案:

@DataJpaTest 只切数据层,默认嵌入式库或 Testcontainers 配真实方言;TestEntityManager _flush 后断言持久化结果。注意事务回滚与懒加载初始化,必要时 @Transactional 在测试类上。

@DataJpaTest
class DatabaseTest {
    @Autowired
    private TestEntityManager entityManager;
    
    @Test
    void testSave() {
        User user = new User("test");
        entityManager.persist(user);
        assertNotNull(user.getId());
    }
}

Q68: Java中如何测试REST API?

答案:

@WebMvcTest 只加载 Web 层,@MockBean 掉下游服务,用 MockMvc 发请求验状态码与 body。与全量 @SpringBootTest 相比更快,适合控制器契约。

@WebMvcTest(UserController.class)
class ApiTest {
    @Autowired
    private MockMvc mockMvc;
    
    @MockBean
    private UserService userService;
    
    @Test
    void testGetUser() throws Exception {
        when(userService.getUser(1L)).thenReturn(new User());
        mockMvc.perform(get("/users/1"))
            .andExpect(status().isOk());
    }
}

Q69: Java中如何测试异步代码?

答案:

CompletableFuture.get 带超时,避免测试永久挂起;或轮询 Awaitility。断言异常完成态与正常完成态两条路径。

@Test
void testAsync() throws Exception {
    CompletableFuture<String> future = asyncMethod();
    String result = future.get(5, TimeUnit.SECONDS);
    assertEquals("expected", result);
}

Q70: Java中如何测试并发代码?

答案:

多线程压测时配合 CountDownLatch 或 CyclicBarrier 对齐起跑,主线程 await 带超时。断言共享数据结构的不变量,而不是依赖线程调度顺序。

@Test
void testConcurrency() throws InterruptedException {
    ExecutorService executor = Executors.newFixedThreadPool(10);
    CountDownLatch latch = new CountDownLatch(10);
    
    for (int i = 0; i < 10; i++) {
        executor.submit(() -> {
            try {
                // 测试代码
            } finally {
                latch.countDown();
            }
        });
    }
    
    assertTrue(latch.await(5, TimeUnit.SECONDS));
}

Q71: Java中如何测试文件操作?

答案:

Files.createTempFile + try-finally 删除,或 JUnit 5 的 @TempDir。断言读写内容与异常路径(权限、不存在)。

@Test
void testFileOperation() throws IOException {
    Path tempFile = Files.createTempFile("test", ".txt");
    try {
        Files.write(tempFile, "content".getBytes());
        String content = new String(Files.readAllBytes(tempFile));
        assertEquals("content", content);
    } finally {
        Files.deleteIfExists(tempFile);
    }
}

Q72: Java中如何测试配置?

答案:

@TestPropertySource 或动态属性覆盖 application.yml,用 @Value / @ConfigurationProperties 注入后断言绑定结果。敏感配置不要写死在仓库。

@TestPropertySource(properties = {"key=value"})
class ConfigTest {
    @Value("${key}")
    private String value;
    
    @Test
    void testConfig() {
        assertEquals("value", value);
    }
}

Q73: Java中如何测试日志?

答案:

可用 appender 内存列表、Logback 的 ListAppender,或 Mockito mock static(JDK 版本与 mock 静态能力要匹配)。断言关键字段是否进日志,别绑完整堆栈字符串。

@Test
void testLogging() {
    Logger logger = LoggerFactory.getLogger(TestClass.class);
    try (MockedStatic<LoggerFactory> mocked = 
         mockStatic(LoggerFactory.class)) {
        mocked.when(() -> LoggerFactory.getLogger(any()))
            .thenReturn(mock(Logger.class));
        // 测试代码
    }
}

Q74: Java中如何测试缓存?

答案:

CacheManager 上 put/get/evict 走一遍,断言命中与淘汰;TTL 场景可配合虚拟时钟或缩短配置。Spring Cache 抽象下注意 @Cacheable 是否走代理。

@SpringBootTest
class CacheTest {
    @Autowired
    private CacheManager cacheManager;
    
    @Test
    void testCache() {
        cacheManager.getCache("test").put("key", "value");
        String value = cacheManager.getCache("test")
            .get("key", String.class);
        assertEquals("value", value);
    }
}

Q75: Java测试的最佳实践有哪些?

答案:

测试类与被测类包结构镜像,命名一眼能看出测谁。单测不连真实外部系统,边界用 Mockito;集成测再上 Testcontainers。断言优先 AssertJ,失败信息可读。@BeforeEach 里重置共享 mock,避免顺序依赖。

一是命名:测试类以 Test 结尾,方法名写清场景。

二是分层:单测快、集成测带容器、E2E 少而精。

三是 Mock 只挡外部,并验证必要交互。

四是断言带业务语义,少用裸 assertTrue(condition)。

最近更新: 2026/5/12 03:06
Contributors: raina
Prev
15-测试文档面试题
Next
17-测试最佳实践面试题