测试编程语言面试题
本文档共 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)。