Robot Framework数据库查询中文乱码:用utf8mb4从源头解决
发布时间:2026/10/21 9:14:03 锦皓数字建站

前阵子做一个用户中心的UI自动化回归用例逻辑本身不复杂从数据库查出用户当前昵称再到页面上比对显示是否一致。结果第一次跑就翻了车——数据库查出来的昵称在日志里是一串三之类的乱码页面上的正常中文跟它比对怎么比都不相等。我一开始以为是断言逻辑写错了排查半天才意识到根本原因是数据库返回的中文字符串在传递过程中没有被正确解码。这个坑在做Robot Framework UI自动化时非常常见网上说法五花八门有让改Python默认编码的有让写自定义关键字强行转码的我把自己实际验证过、也最推荐的办法整理了出来。这是系列里的第一种方案核心思路很朴素从数据库连接入口把编码问题解决掉而不是等乱码出现了再到处打补丁。1. 乱码问题的根因定位先别急着写代码很多人遇到乱码第一反应是“在断言之前做个转码”比如把字符串encode再decode一下。这个思路不能说错但属于治标不治本——你根本不知道乱码是哪一层产生的转码方向不对反而会把原本正常的中文转坏。我更建议先花几分钟理清整条链路定位到具体环节之后再动手。1.1 编码链路数据库、驱动、Python、日志各管一段一次完整的数据库查询从写入到显示至少要经过四个环节数据库服务器端的存储字符集和表字段的collation数据库客户端连接时协商使用的字符集Python数据库驱动接收查询结果后如何解码成字符串Robot Framework把字符串写入日志文件和输出到控制台时使用的编码打个比方这就像快递包裹从仓库发到你家包裹上的中文地址能不能被看懂取决于仓库出库单用的什么编码、快递员手里的扫码枪怎么解码、最后贴在门上的面单又是怎么打印的。如果中间任何一个环节用了不匹配的编码中文就变成了乱码。所以排查时不能只盯着一处。在MySQL里最关键的几个变量是character_set_client、character_set_connection和character_set_results。很多情况下数据库表本身是utf8mb4但客户端连接时没有显式指定字符集驱动就用了默认的latin1或者系统区域设置的编码去解释返回的字节流结果自然是一堆问号或者类似三的乱码。1.2 怎么快速确认乱码出在哪一层我在实际项目里常用的定位方法很简单三步走用数据库管理工具执行同样的SQL看返回的中文是否正常。如果管理工具正常说明数据库存储本身没问题。单独写一个极简Python脚本用同样的连接参数执行查询print出结果。如果脚本输出乱码说明问题出在驱动连接配置。如果脚本输出正常但Robot Framework的日志或控制台乱码问题则出在Robot Framework运行环境的输出编码。我遇到的情况就是第2步就露馅了脚本里直接输出就是乱码所以问题根本不在Robot Framework而在数据库连接参数。这个定位过程最好写在一张便签上以后不管换了什么数据库、什么项目都能按这个顺序排查避免在错误的方向上反复折腾。2. 把连接参数调对从源头解决大半问题定位到是连接参数的问题之后剩下的工作就比较明确了给数据库驱动一个明确的字符集配置。这一步做完整条链路的后半程基本就不需要再做任何转码操作。2.1 数据库连接库的选择与安装Robot Framework本身不带数据库操作能力最常用的是DatabaseLibrary这个第三方库。它底层依赖Python的数据库驱动对MySQL来说一般用PyMySQL就够了。安装命令我直接贴出来pip install robotframework robotframework-seleniumlibrary robotframework-databaselibrary pymysql安装完成之后在Robot Framework的Settings表格里引入*** Settings *** Library DatabaseLibrary要注意的是DatabaseLibrary的底层实现依赖dbapi模块所以必须在同一Python环境里装好对应的数据库驱动。如果你用的是Oracle或者SQL Server驱动要换成对应厂家的思路一样但连接的写法会有差别。2.2 用Custom Params方式传递charset参数DatabaseLibrary提供两个常用的连接关键字一个是Connect To Database另一个是Connect To Database Using Custom Params。前者适合参数固定、标准的情况但它对非标准连接参数的支持不够直观后者可以通过自定义参数字符串把charset这样的参数直接传进去。我推荐做法是这样的*** Test Cases *** 数据库连接示例 Connect To Database Using Custom Params ... pymysql ... dbapp_db, usertest_user, passwordyour_password, host127.0.0.1, port3306, charsetutf8mb4关键技术点就在最后那个charsetutf8mb4。它告诉PyMySQL客户端向服务器发送请求、以及接收结果时都按utf8mb4字符集来解码。这样从数据库服务器返回的中文字节流就能正确还原成Python字符串。有人可能会问utf8和utf8mb4有什么区别简单说utf8mb4是MySQL对完整Unicode的支持它能存储四字节的字符比如Emoji表情和一些生僻字。而utf8在MySQL里最多支持三字节表情这类字符会丢失或者变问号。除非你确定表里绝对没有表情符号否则连接参数直接用utf8mb4是最省心的选择。设置好连接之后每次查询返回的结果都是正常解码后的str后续的断言、日志输出基本都是顺理成章的事。这也是我把这个方案放在“第一种”的原因它从源头解决了问题而不是在问题发生之后再去补救。3. 实战演练一个完整的UI断言场景这一节我把一个真实项目里跑过的场景完整复现一遍。假设被测系统是一个用户中心用户修改昵称后导航栏需要显示最新的昵称。测试目标是从数据库查出该用户最新的昵称作为期望值再打开页面读取导航栏的实际文本断言两者一致。3.1 准备数据库表结构这里我用一个极简的表结构做演示CREATE TABLE user_profile ( user_id INT PRIMARY KEY, nickname VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ) DEFAULT CHARSETutf8mb4; INSERT INTO user_profile (user_id, nickname) VALUES (1001, 测试用户中文昵称);建表时显式加上CHARACTER SET utf8mb4保证存储层就是正常的Unicode。这一步看似基础但恰恰是很多项目的坑——表建得早当时的默认字符集是latin1后面数据写进来虽然没有报错但实际已经存坏了。如果遇到这种情况单纯改连接参数也没用得先把脏数据清掉再重新写入。3.2 测试用例完整代码下面是一段可直接参考的用例*** Settings *** Library SeleniumLibrary Library DatabaseLibrary Library String *** Test Cases *** 验证用户昵称在页面正确显示 [Setup] Connect To Database Using Custom Params ... pymysql ... dbapp_db, usertest_user, passwordyour_password, host127.0.0.1, port3306, charsetutf8mb4 ${expected_nickname} Query SELECT nickname FROM user_profile WHERE user_id 1001 ${expected_nickname} Set Variable ${expected_nickname[0][0]} Open Browser http://localhost/demo/user-center chrome ${actual_nickname} Get Text iduser-nickname Should Be Equal As Strings ${actual_nickname} ${expected_nickname} [Teardown] Run Keywords ... Close Browser AND ... Disconnect From Database这里有几个细节要专门说明Query返回的是一个列表列表每个元素是元组所以${expected_nickname[0][0]}取的是第一行第一列的值。Set Variable在这里不是必需的但显式赋值能让后续逻辑更清晰也方便调试时单独打印。Should Be Equal As Strings是大小写不敏感的比较。如果页面上的中文和数据库里的中文存在全角半角差异这里可能仍然失败原因不在编码而是数据规范化的问题后面一节我会细说。3.3 运行结果与验证方法正常跑完之后Robot Framework生成的log.html里中文昵称应当以正常中文显示页面元素文本和数据库期望值完全匹配断言通过。为了确认修改连接参数确实生效我习惯在用例里临时加一行Log把期望值打出来Log ############################################# Log ${expected_nickname} Log ${actual_nickname}如果看到两个日志里的中文一致说明编码链路已经通了。如果log.html里正常但控制台输出乱码那属于终端编码的问题和数据库无关可以忽略也可以顺手设置环境变量PYTHONIOENCODINGutf-8解决。4. 常见问题与排查技巧实录这个方法在实际落地中我自己也遇到过一些边边角角的问题。下面把最常见的现象、原因和解决方案整理成一个速查表方便日后对照。4.1 乱码问题速查表现象可能原因解决办法数据库管理工具正常Robot Framework日志乱码连接时没有指定字符集改用Connect To Database Using Custom Params传charsetutf8mb4log.html正常控制台输出乱码终端使用的是GBK或系统区域编码设置环境变量PYTHONIOENCODINGutf-8或只依赖日志排查返回结果带b...前缀看起来是bytes驱动的charset参数未生效返回未解码的字节流检查连接参数是否真的被传递必要时自定义Python库断言失败但页面上和数据库里的文本肉眼看起来一样存在全角半角、不可见字符或Unicode空格先调用String.Trim String或Normalize Unicode后再比较修改连接参数后仍然乱码表本身存储的就是乱码数据检查表字符集和字段collation清理源数据4.2 三个容易踩的坑第一个坑是只加了charsetutf8而没有用utf8mb4。某次我在一个老项目里遇到用户昵称带了一个Emoji结果页面显示正常数据库连接也写了utf8但查询出来的Emoji变成了问号。原因就是MySQL的utf8不支持四字节字符换成utf8mb4之后才正常。第二个坑是把连接参数的字符集和Python环境默认编码混为一谈。Robot Framework本身是一个Python进程它的stdout编码、日志文件的写入编码和数据库连接返回的字符串编码是两回事。数据库连接问题会直接在查询结果上体现而Python环境编码问题更多体现在控制台输出和文件写入上。我见过有人在测试脚本里写一大堆# -*- coding: utf-8 -*-和sys.setdefaultencoding结果数据库查询结果还是乱码就是因为没抓到真正的问题源头。第三个坑是过度依赖事后转码。比如从数据库拿到一个乱码字符串试图用.encode(latin1).decode(utf-8)把它救回来。如果运气好确实能还原但这类转码方法极度依赖原始字节流和当前显示乱码之间的可逆关系。一旦字段内容里混入了其他字符集转码结果会更加混乱。正确的思路是保证从数据库驱动到Python字符串这一段链路完全正常后面的处理和编码问题无关。4.3 通用排查流程如果你刚接手一个老项目数据库已经连上了但中文乱码久治不愈可以按这个顺序排查先看数据库和表的字符集确认存储侧没问题。单独用Python脚本连接数据库打印一条包含中文的查询结果。如果脚本乱码调整连接参数确认驱动配置。如果脚本正常但Robot Framework乱码检查Robot Framework运行环境输出编码。最后再考虑数据本身是否存在混入的异常字符比如不可见字符或者全角空格。按照这个顺序每一步都能缩小范围。最忌讳的就是一上来就写各种转码代码最后越改越乱。结尾说实话这个问题的解决方案本身不复杂但它很能说明一个道理自动化测试中的很多“疑难杂症”根源往往不是在测试代码层而是在基础组件的配置层。我在实际使用中发现把数据库连接参数一次调对比在用例里到处写Evaluate做转码要省事得多而且可维护性也好得多——后来团队其他人接手这套用例时不需要了解什么转码黑魔法只要照葫芦画瓢把charsetutf8mb4写上中文就一直是正常的。如果后续你还会遇到Oracle或者SQL Server下的同类问题处理思路其实一致只是连接参数的写法有所不同。先把这里说的第一种方案用上大多数情况下就已经够了。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。