每年一到秋招季,赛码网上的在线笔试就成了大厂筛人的第一道关。我参加了2023年腾讯音乐秋招技术测试岗的第二批笔试,整体感受是:题量不大但覆盖面广,难度介于校招常规水平和进阶之间,尤其侧重测试思维和工程落地能力。这篇文章我不打算只给答案,而是把当时拆题、选题、写代码的完整思路和踩坑过程都复盘一遍,给后面准备大厂测试岗笔试的同学一份能直接参考的作战手册。
1. 考试整体情况与设计思路
1.1 腾讯音乐测试岗到底考什么
先说个很多人容易搞错的点:腾讯音乐的技术测试岗,笔试内容和纯开发岗有明显区别。开发岗更看重算法功底和代码熟练度,而测试岗会额外考察你对“质量”的理解——比如怎么设计测试用例、怎么定位线上问题、怎么评估一个功能模块的风险点。
第二批笔试的题型大致分三块:单选题、多选题、编程题。选择题覆盖了数据结构、操作系统、计算机网络、数据库、软件测试理论;编程题通常是两道,一道偏算法,一道偏场景实现。整体时长从考试系统倒计时看是120分钟,时间不算宽裕,尤其是编程题需要留足调试时间。
我当时拿到试卷的第一反应是:选择题部分有不少“送分题”和“陷阱题”混在一起,比如TCP三次握手的细节、数据库索引失效的场景、等价类划分的边界值选取这些。这类题没有太多弯弯绕绕,但如果你基础不牢或者审题不仔细,很容易在“看似简单”的题上翻车。建议后续准备的同学,把软件测试理论(等价类、边界值、判定表、场景法)和计算机网络、操作系统的常规考点牢牢抓住,这三块是出题重灾区。
1.2 第二批相比第一批的难度变化
腾讯音乐的秋招笔试是分批次进行的,第一批和第二批之间并不是简单的“换一套题”,而是会根据前一批的通过率做难度微调。从我身边同学反馈和论坛上的讨论来看,第二批的编程题比第一批更偏向“实际业务场景”,不再是纯粹的LeetCode式算法题,而是给你一个功能需求,让你写一个能跑的解决方案。
比如第二批遇到了一道和字符串处理、日志分析相关的场景题,考点不在于你用了多高级的算法,而在于你能不能写出逻辑严谨、边界处理完整的代码。这个思路其实和测试岗的日常工作非常贴近——测试工程师经常要写脚本处理日志、解析接口返回值、批量造测试数据,代码能力不要求顶尖,但必须可靠、可维护。
所以我的建议是:不要只刷算法题,还要练习“用代码解决具体小问题”的能力,尤其是字符串处理、文件读写、简单的数据结构操作。这种能力在笔试中比背模板更管用。
2. 核心知识点拆解与备考重点
2.1 选择题覆盖的六类考点
我把这次笔试的选择题考点做了个分类,基本可以归纳成六类,每一类都要针对性准备:
第一类是软件测试基础理论。比如黑盒测试和白盒测试的区别、单元测试和集成测试的侧重点、回归测试的触发时机、缺陷生命周期的状态流转。这类题没有难度,但非常考验记忆的准确性。
第二类是数据结构与算法。常见的有数组和链表的区别、栈和队列的应用场景、二叉树遍历方式、哈希表解决冲突的方法、排序算法的时间复杂度对比。这类题对科班同学来说应该是送分题,但非科班同学需要专门补一下。
第三类是操作系统。进程和线程的区别、死锁产生的四个必要条件、虚拟内存和分页机制、常见的进程调度算法。腾讯音乐这个岗位日常要跟客户端、服务端打交道,理解这些概念对后续工作很有帮助。
第四类是计算机网络。TCP和UDP的区别、TCP三次握手和四次挥手的过程、HTTP和HTTPS的差异、DNS解析流程、常见的状态码含义。这类题在测试岗笔试中出现频率极高,因为测试过程中经常要分析网络请求。
第五类是数据库。SQL基本语法、索引的优缺点和失效场景、事务的ACID特性、隔离级别和对应的问题(脏读、不可重复读、幻读)、表连接的不同类型。数据库这块几乎是必考的,而且经常结合场景出题。
第六类是逻辑思维题。比如给一段程序让你推断输出结果,或者给一个业务场景让你判断哪个测试用例设计得最合理。这类题没有固定考点,靠的是平时积累和临场思维敏捷度。
2.2 多选题的“全选”陷阱和漏选扣分
多选题是这场笔试里最容易让人纠结的部分。腾讯音乐的计分规则是多选、少选、错选都不得分,这就意味着你不能用“排除法后蒙一个”的策略,必须确保每个选项都有把握才选。
我印象比较深的是一道关于“测试用例设计方法”的多选题,选项包括等价类划分、边界值分析、因果图、错误推测法。前三个显然是正确的,但第四个“错误推测法”其实是测试用例设计中确实存在的方法,只是在某些定义版本里它被归为经验型方法而非正式的设计方法。如果你对这个知识点掌握得不够精确,很容易在“要不要选它”之间纠结。
应对多选题的策略有三个。第一,优先选择你有十足把握的选项,没有把握的宁可不选;第二,遇到“绝对化”表述的选项(比如“一定会”“必须”“所有”)要多留个心眼,很多时候命题人就是在这里埋坑;第三,平时复习时要把相似概念区分开,比如“测试用例设计方法”和“测试方法”就是两个不同的体系,不能混为一谈。
2.3 测试理论复习的优先级排序
如果你准备时间有限,不知道从哪儿下手,我建议按这个优先级来复习测试理论:等价类划分和边界值分析(必考且易得分)→ 场景法和判定表法(次重点)→ 黑白盒测试的具体方法(选择题常客)→ 测试流程和缺陷管理(偶尔出现)。
等价类和边界值之所以重要,是因为它们是所有测试设计的基础,不仅是笔试,面试中也会反复问到。复习时不能只背定义,要能举例说明。比如一个输入框要求输入1到100的整数,等价类可以划分成有效等价类(1-100之间的整数)和若干无效等价类(小于1的数、大于100的数、非整数、非数字字符),边界值则是1、100、0、101这四个值。这种“输入域分析”的能力,在笔试的案例题里会直接考察。
另外提醒一点:现在很多笔试题目喜欢把测试理论“包装”成场景题。比如“你是一个音乐App的测试工程师,评论区新增了一个@功能,请设计测试用例”。这种题的本质还是在考等价类和边界值,只是载体变成了业务场景。所以复习时要有意识地练“把业务描述翻译成测试点”的能力。
3. 笔试实操过程与真题思路复盘
3.1 赛码网笔试环境与时间分配建议
腾讯音乐这次笔试用的赛码网平台,需要在浏览器里完成整个答题过程。赛码网有几个特点你需要提前适应:第一,选择题页面和编程题页面是分离的,可以来回切换,但切换过程中不会保存你的草稿;第二,编程题的代码编辑器支持C++、Java、Python等多种语言,但不会给你本地编译环境,代码写完后要在网页上点击“运行”来测试;第三,编程题的输入输出必须严格按照题目要求处理,多一个空格、少一个换行都可能判错。
时间分配上,我的经验是把120分钟切成三段:前40分钟搞定所有选择题,中间30分钟做第一道编程题,接下来30分钟做第二道编程题,最后20分钟检查。这个节奏可能因人而异,但核心原则是“选择题不要恋战”——如果一道选择题卡了超过3分钟,先标记一下,跳过去做后面的,等编程题写完再回头琢磨。
我当时在时间分配上就吃了点亏。前面有两道多选题纠结太久,导致编程题开始时间比计划晚了10分钟左右。好在编程题本身难度不算特别大,最后有惊无险地完成了。这里特别提醒:赛码网的编程题运行环境是Linux,编译器版本可能和你本地环境有差异,代码里尽量不要用平台相关的特性。
3.2 第一道编程题:日志异常检测与统计
这道题的大意是:给定一个日志文件,每行包含时间戳、日志级别(INFO/WARN/ERROR)和日志内容,要求统计每个日志级别出现的次数,并按出现次数从高到低输出;如果出现次数相同,按日志级别的字典序输出。
这类题目在真实工作中非常常见,所以我看到题目的时候甚至有点亲切。解题思路分三步走:先按行读取日志文件,然后解析每行中间的日志级别字段,最后用一个Map统计次数并排序输出。需要注意的是,日志级别字段的前后可能有多个空格,不能简单用空格分割取第二个字段,建议用正则表达式或者split方法配合trim处理。
下面是我当时的核心代码逻辑(Java版):
import java.io.*; import java.util.*; public class LogAnalyzer { public static void main(String[] args) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); Map<String, Integer> countMap = new HashMap<>(); String line; while ((line = reader.readLine()) != null && !line.isEmpty()) { String[] parts = line.split("\\s+"); if (parts.length >= 2) { String level = parts[1]; countMap.put(level, countMap.getOrDefault(level, 0) + 1); } } // 按次数降序,次数相同按字典序升序 List<Map.Entry<String, Integer>> list = new ArrayList<>(countMap.entrySet()); Collections.sort(list, (a, b) -> { if (a.getValue() != b.getValue()) { return b.getValue() - a.getValue(); } else { return a.getKey().compareTo(b.getKey()); } }); for (Map.Entry<String, Integer> entry : list) { System.out.println(entry.getKey() + " " + entry.getValue()); } } }这里有几个容易踩的坑。第一,题目给的日志格式可能包含前后空格,而split("\s+")能自动处理,你千万别用split(" ")然后去判断数组长度,那样很容易越界。第二,日志文件可能有多行,测试数据可能包含“ERROR ERROR”这种日志级别重复的内容——题目里级别字段就一个,但如果你以为每行只有一个日志级别,可能做不对。第三,输出时最后一行要不要换行,不同题目要求不一样,赛码网通常不检查行尾换行,但保险起见我建议最后一行也输出换行符。
实际上,这道题我在做的时候还考虑到一种情况:如果日志级别不是标准的INFO/WARN/ERROR,而是自定义的其他级别怎么办?题目原文里说了“假设日志级别由大小写字母组成”,所以我的代码实际上能处理任意字符串级别的统计。这种“假设条件的理解能力”也是考察点之一。
3.3 第二道编程题:用户行为序列的最长连续活跃天数
这道题类似“最长连续递增子序列”的变体。题目描述是:给定一个用户一年内的活跃日期数组(日期格式为“MM-DD”),要求计算最长的连续活跃天数。闰年和跨年不算,保证所有日期都在同一年内。
这道题的核心思路是把日期转换成一年中的第几天,然后排序,再求最长连续递增序列。日期转换有一个标准公式,也可以用Java的LocalDate类或者自己写一个简单的映射表。因为我当时选择用Python写这道题,代码非常简洁:
def days_of_month(): return [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] def date_to_day(date_str): month, day = map(int, date_str.split('-')) days = days_of_month() return sum(days[:month-1]) + day def longest_active_streak(dates): days = sorted(set(date_to_day(d) for d in dates)) if not days: return 0 max_len = 1 cur_len = 1 for i in range(1, len(days)): if days[i] == days[i-1] + 1: cur_len += 1 max_len = max(max_len, cur_len) else: cur_len = 1 return max_len n = int(input()) dates = [input().strip() for _ in range(n)] print(longest_active_streak(dates))这道题真正的难点其实不在算法本身,而在两个容易被忽略的边界条件。第一,日期数组可能包含重复日期,比如同一天有多条活跃记录。你没去重之前,连续活跃天数的计算会出错——如果同一天出现了两次,排序后它们是相邻的,但它们的差值不是1,程序会误判为“连续中断”。当时我就在这道题上纠结了一会儿,后来加了一个set去重才解决。
第二,日期格式中的“MM-DD”可能包含前导零,比如“01-05”和“1-5”如果混在一起,直接按字符串转int是可以的,但如果你用字符串比较,就会因为“01-05”小于“1-5”而排序错误。所以处理时一定要先split再转int,不能直接比较字符串。
这里也顺带说一个小技巧:处理日期类题目时,把日期转换成整数天序号几乎是万能的解法,无论你是求差值、求连续天数还是求区间重叠,都能用这个统一思路。
3.4 编程题的通用解题模板与调试策略
总结这次笔试的两道编程题,我提炼出一个适合测试岗笔试的通用解题模板:
第一步,快速读题并画出输入输出格式。不要急着写代码,先用2分钟确定题目的输入是什么、输出是什么、有没有特殊的格式要求。
第二步,确定数据结构。绝大多数笔试编程题都能归入“数组处理”“字符串处理”“模拟过程”三类。数组处理优先想排序+双指针,字符串处理优先想split+Map,模拟过程就老老实实按步骤写。
第三步,写代码前先写几个测试用例。哪怕只是空输入、单个元素、全重复元素这种极端情况,也先在脑子里过一遍,这样能避免很多低级错误。
第四步,代码写完不要马上提交,先用自己的测试用例跑一遍。赛码网的返回结果可能只告诉你“通过率XX%”,不会告诉你具体错在哪,所以你必须自己提前演习。
在调试策略上,我的习惯是:能用print打印中间结果的,先打印出来看看;如果在线环境不支持调试,就多用几个System.out或者print的日志,等确认逻辑无误后再删除。这个习惯在这次笔试中帮我避免了一次低级错误——第一道题我第一次运行后输出结果为空,检查后发现是正则表达式写错了,把split("\s+")写成了split("\s*"),导致整行被拆成一个字符串。
4. 常见易错点与备考实用建议
4.1 赛码网笔试最容易踩的坑
赛码网作为一个在线笔试平台,和牛客网、LeetCode的体验有些不同,我用过一次之后就明显感觉到,有些坑你只有亲自踩过才记得住。
第一个坑是本地编译和在线编译不一致。比如Java环境下,本地用的JDK 17,赛码网可能用的JDK 8,你用了var关键字或者Stream.toList()这类JDK 9+的API,本地编译没问题,但在线编译直接报错。我当时写代码时下意识用了var,后来意识到版本问题才反过来改成显式类型。这个建议真的很重要:笔试前先看看赛码网支持的编译版本清单,Java就老老实实用JDK 8的语法,C++就把新特性全关掉,Python就优先用Python 3.9+的语法但别依赖太新的库。
第二个坑是输入读取方式。赛码网的输入通常通过标准输入读取,可能是多行、可能是一行多个数据、可能列数不固定。如果你用的是Scanner,处理大量数据时速度会比较慢,但笔试数据量一般不大,问题不大。如果你用的是BufferedReader,要注意readLine()读取最后一行的可能为null,循环条件要写成(line = reader.readLine()) != null。
第三个坑是输出的格式。很多同学本地测是好的,提交上去反而报错,大概率是输出格式不一致。比如题目要求“数字之间用空格隔开”,你输出了制表符;或者要求“按行输出”,你最后多打印了一个空行。赛码网对格式的校验有时严格到“多了一个空格也算错”。我的做法是:不管题目要求多简单,写完代码后,把输出复制到记事本里,数一下空格和换行,确认和样例输出完全一致再提交。
4.2 测试岗笔试与开发岗笔试的三大区别
很多同学会问:我准备了Java开发岗的笔试内容,能不能直接去考测试岗?我的回答是:可以,但不完全适用。两者至少有三大区别,你必须要清楚。
第一,考察侧重点不同。开发岗更看重你“能不能把这个功能做出来”,而测试岗更看重你“能不能判断它做得对不对、好不好、有没有边界问题”。所以测试岗的编程题往往不只是算法题,还会带一点“业务理解”的要求。
第二,测试理论的占比不同。开发岗笔试几乎不考测试理论,而测试岗必考。这些理论说难不难,但如果你没系统复习过,临场只能靠猜,正确率会让你血压升高。
第三,多选题和场景题的比重不同。测试岗笔试经常出现“以下哪些测试用例设计得合理”“这个缺陷应该归为哪个优先级”这类题目,这考察的是测试思维,而测试思维不是刷算法题能刷出来的。你需要在日常学习中有意识地去想“这个功能如果是我测,我会怎么设计用例”,而不是只盯着代码覆盖率。
4.3 备考时间短,优先刷哪些内容
如果你是临时决定投递测试岗,只有一两周准备时间,我建议你按这个优先级来安排:
先从“软件测试理论”开始,因为这是测试岗的立身之本,考的概率最大。用两天时间把等价类划分、边界值分析、因果图、判定表、场景法、错误推测法这些基础方法过一遍,再配合几道练习题巩固。
然后花三天时间复习“计算机基础”,重点是计算机网络(TCP/UDP、HTTP/HTTPS、DNS)和操作系统(进程线程、死锁、内存管理)。数据库的SQL语法稍微练一下,能写基本的增删改查和join语句就行。
剩下时间全部用来刷“编程题”。不要追求难题偏题,把LeetCode的“简单”和“中等”难度题刷一遍,重点看字符串处理、数组操作、排序、哈希、双指针这些常规题型。测试岗的编程题不太可能出特别复杂的动态规划或图论,但你要保证常见的题型都能在20分钟内写完并测试通过。
我当时在最后一周做的一件事是:每天花30分钟把当天的编程题“口头复述”一遍自己的解题思路,包括用什么数据结构、为什么这么选、边界条件有哪些。这个习惯让我在做第二道编程题时,几乎没有卡壳就把逻辑写出来了。口头复述的过程其实是在强化你的“结构化思维”,在考试时能让你更快地找到切入点。
4.4 从笔试反推回来的几条学习心得
参加完这次腾讯音乐测试岗笔试,我最大的一个感受是:大厂笔试不只是在“考知识”,更是在“模拟工作”。那些选择题里的测试理论、编程题里的日志处理,本质上都是你入职后会遇到的真实工作场景。所以准备笔试的过程,其实也是在提前适应岗位。
另一个感受是:基础知识的掌握程度决定了你的上限。这次笔试里有好几道选择题,如果你对知识点的记忆是模糊的、似是而非的,做起来会非常难受。比如TCP三次握手中的状态变化、数据库索引在什么情况下会失效,这些知识如果只看过没理解,考试时就是“看着哪个选项都像对的”。因此不只是为了笔试,为了后续的面试和实际工作,都值得多花一点时间把基础概念吃透。
最后想说的是,笔试只是秋招的第一道关,就算你通过了,后面还有面试、HR面等好几轮。但笔试的通过率通常是最低的,很多人倒在了这一步。把心态放平,把该准备的内容准备好,剩下的就交给临场发挥了。如果这篇复盘能帮你少踩几个坑,那这篇内容就没白写。