Python单元测试模拟X509证书遇PEM报错,求解决方案
单元测试模拟证书读取时OpenSSL.crypto.Error问题的解决方法
原函数代码
def get_private_key(): with open(config.CERTIFICATE_PATH + config.PRIVATE_KEY, 'rb') as key_file, \ open(config.CERTIFICATE_PATH + config.PUBLIC_KEY, 'rb') as cert_file_string: private_key = serialization.load_pem_private_key(key_file.read(), password=None, backend=default_backend()) cert = load_certificate(FILETYPE_PEM, cert_file_string.read()) sha1_fingerprint = cert.digest("sha1") strkid = sha1_fingerprint.decode() kid = strkid.replace(':', '')
测试代码及错误
测试代码:
@patch('OpenSSL.crypto.load_certificate') @patch('cryptography.hazmat.primitives.serialization.load_pem_private_key') @patch('builtins.open') def test_get_private_key(self, mock_open, mock_load_pem_private_key, mock_load_certificate) mock_open.return_value = io.BytesIO('dummy'.encode('utf-8')) mock_load_pem_private_key.return_value = MagicMock(cryptography.hazmat.backends.openssl.rsa.RSAPrivateKey) mock_load_certificate.return_value = MagicMock(crypto.X509) self.assertEqual(get_private_key(), "dummyPvtKey")
运行时错误:
OpenSSL.crypto.Error: [('PEM routines', 'get_name', 'no start line')]
问题原因及解决步骤
1. 修正patch路径(核心问题)
错误出现的根本原因是patch的目标路径不正确:你需要patch的是被测试函数所在模块中实际导入的load_certificate对象,而非原始的OpenSSL.crypto.load_certificate。
- 假设
get_private_key位于your_project.auth.utils模块,且该模块中导入语句为from OpenSSL.crypto import load_certificate,则测试中的patch应改为:@patch('your_project.auth.utils.load_certificate') - 如果模块中是
import OpenSSL.crypto并通过OpenSSL.crypto.load_certificate调用,则patch路径应为your_project.auth.utils.OpenSSL.crypto.load_certificate
2. 修复多文件打开的mock逻辑
原测试中mock_open.return_value只返回一个BytesIO实例,但函数同时打开了两个文件(私钥和证书),需要让mock_open按顺序返回不同的模拟内容:
mock_open.side_effect = [ io.BytesIO(b'-----BEGIN PRIVATE KEY-----\ndummy_key_content\n-----END PRIVATE KEY-----'), io.BytesIO(b'-----BEGIN CERTIFICATE-----\ndummy_cert_content\n-----END CERTIFICATE-----') ]
这样即使patch未完全生效,也能避免因内容不符合PEM格式触发解析错误。
3. 完善mock对象的方法返回值
原函数最终返回的是证书SHA1指纹去除冒号后的字符串,需要让mock的证书对象返回符合预期的digest结果:
# 模拟证书对象的digest方法返回值 mock_cert = MagicMock() mock_cert.digest.return_value = b'12:34:56:78' mock_load_certificate.return_value = mock_cert # 对应修改断言 self.assertEqual(get_private_key(), "12345678")
4. 修复测试函数的语法错误
原测试函数定义末尾缺少冒号,修正为:
def test_get_private_key(self, mock_open, mock_load_pem_private_key, mock_load_certificate):
内容的提问来源于stack exchange,提问作者Ahmad Shaikh
相关产品推荐
相关产品推荐

