生活知识集
第二套高阶模板 · 更大气的阅读体验

单元测试中如何处理依赖外部服务的问题

发布时间:2025-12-14 11:46:59 阅读:232 次

写代码时,单元测试是个好习惯。但很多时候,我们的函数会依赖外部服务,比如调用天气 API、支付接口或者用户登录系统。这时候直接跑测试,速度慢不说,网络一断就报错,根本没法稳定运行。

问题在哪?

举个例子,你写了个方法 checkWeatherStatus(),它会请求某个天气网站的接口,判断今天是否下雨。如果真去发请求,每次测试都得联网,而且万一那个网站抽风,你的测试就挂了——可其实你的逻辑没问题。

单元测试的核心是“隔离”,只测自己的逻辑,不碰外部环境。所以,得想办法把外部依赖“替身”掉。

用 Mock 模拟外部响应

常见的做法是使用 Mock 技术,假装那个接口返回了数据。比如在 Python 的 unittest 中,可以用 unittest.mock.patch 来替换实际的网络请求。

from unittest import mock
import unittest

# 假设这是你要测试的函数
def check_weather_status(city):
    response = requests.get(f"https://api.weather.com/v1/{city}")
    data = response.json()
    return "rain" in data["condition"].lower()

# 测试类
class TestWeather(unittest.TestCase):
    @mock.patch('requests.get')
    def test_rainy_condition(self, mock_get):
        # 设定模拟返回值
        mock_get.return_value.json.return_value = {
            "condition": "Light Rain"
        }
        
        result = check_weather_status("shanghai")
        self.assertTrue(result)

这样,测试运行时根本不会真的发请求,而是用你设定的假数据。速度快,还稳定。

前端也能这么做

如果你是写 JavaScript,比如用 Jest 测试一个获取用户信息的函数,也可以 mock 掉 fetch。

global.fetch = jest.fn(() =>
  Promise.resolve({
    json: () => Promise.resolve({ id: 1, name: '张三' }),
  })
);

// 然后测试你的函数
test('should return user name', async () => {
  const userData = await fetchUser(1);
  expect(userData.name).toBe('张三');
});

这样一来,哪怕服务器没开,测试照样能跑。

别忘了边界情况

除了正常返回,还要考虑网络超时、接口报错、返回空数据这些情况。Mock 的好处就是你能轻松模拟这些“倒霉场景”。

@mock.patch('requests.get')
def test_network_error(self, mock_get):
    mock_get.side_effect = requests.exceptions.ConnectionError
    
    with self.assertRaises(ConnectionError):
        check_weather_status("beijing")

这种测试平时很难触发,但用 Mock 几行代码就能覆盖,提前发现容错问题。

小技巧:封装外部调用

建议把所有外部请求单独封装成一个模块或类。比如写个 ApiService,专门处理 HTTP 调用。这样测试时只需要 mock 这一层,不用动业务逻辑。

就像修电脑时,你不拆主板,只换插件卡。干净利索,还不影响其他功能。

实际开发中,很多人一开始图省事直接写死请求,结果后期加测试特别麻烦。早点把外部依赖隔离出来,后面省心不少。