认识单元测试
单元测试是什么?
单元测试(UT)是测试系统中的一环,测试系统还包含很多其它环,例如:端到端测试E2E、集成测试Integration、静态检查Lint。
日常工作中,每个业务版本都在进行着这样的一个流程:需求分析 > 代码设计 > 开发 > 测试 > 上线。其中,测试环节是为了检查代码是否符合预期、是否能正常工作。对于前端而言,测试手段至少有这4种:
- 端到端测试(End to End,即E2E):
用一个机器人/脚本,完全模仿真实用户去访问整个系统。(前后端都被覆盖)
- 集成测试(Integration):
Mock掉后端、可能也Mock掉前端部分耗时操作(eg:动画),测试前端部分的输入/输出是否正确。(前端被覆盖)
- 单元测试(Unit,即UT):
针对一个代码模块、代码函数,测试输入/输出是否正确。(某个模块被覆盖)
- 静态检查(Static):
语法检查,TypeScript、ESLint等。
这4种测试手段的成本不同,收益也不同。越往上信心越大。但同时,实施开销也越大,写用例和维护用例的时间越长,用例越容易崩,且崩了的定位和修复精力耗费越多,用例执行速度也越慢。
那么,什么时候应该使用 UT呢?这取决于我们要针对的场景。这种选择,本质上是在做成本和收益的权衡。
不同的手段有不同的擅长点,选择正确的测试策略是大前提。这就像刷墙,面对一面凹凸不平的墙,如果只用滚筒刷,那么细节将得不到覆盖;如果只用小刷子,那么人会被累死。
而我们搭建测试系统,也是这个道理:
- 如果想测试商品购买页中,组合商品的价格是否正确,我们应该选择 E2E
- 如果想验证参数配置页中,参数间的关联关系是否正确,我们应该选择 Integration
- 如果想验证 i18n模块,在不同语种下取词是否正确,我们应该选择UT
- 如果想检查 format函数的所有调用点,是不是都传入了一个string,我们应该选择Typescript
所以,在决定使用UT之前,梳理一下自己的业务,列出最重视的功能,思考最痛的痛点,给它们找一个合适的测试手段。不要企图用一种手段去解决所有的问题,而应该组合使用这些手段来编织一张防护网:通过E2E去覆盖核心功能点,通过Integration去覆盖前端逻辑,通过UT去覆盖核心模块,通过静态检查去守护每一行代码 。
(PS: 或许不同的人会有不同的分类观点,但怎么分类并不是关键,关键是我们应该组合使用各种手段,用UT去做UT最擅长的事儿,而不是一招鲜吃遍天的哪哪都靠UT)
单元测试应该测什么?
单元测试不应该测细节,搞清楚我们的“用户”是谁,“用户”怎么用,我们就怎么测。
由于代码覆盖率指标的驱动,我们很容易走进测细节的死胡同。例如,针对下面这段代码写UT,可能会写出这样的测试用例
// 这个函数用来过滤undefined和null
function filterEmpty(arr) {
if (Array.isArray(arr)) {
return arr.filter(item => item);
} else {
return [arr].filter(item => item);
}
}
// 用例1:要想进if,arr必须是个数组
expect(filterEmpty([1, 2, 3])).toEqual([1, 2, 3]);
// 用例2:要想进else,arr必须是非数组
expect(filterEmpty(1)).toEqual([1]);
这里,虽然2个用例覆盖了被测代码的所有行,但arr中如果有0、空字符串、false 也会被过滤掉。所以,针对代码细节写测试,即使覆盖率100%,也无法给我们提供足够的信心。这是因为,测代码细节,无法避免假正确(用例跑过了,但功能不通)和假错误(用例没通过,但功能是好的) 。这种用例没法给我们带来代码信心,只会徒增工作量,而这可能是很多人不喜欢写UT的原因。
那么,如何避免陷入测细节的陷阱呢?答案是:搞清楚我们的“用户”是谁。如果被测模块是个交互组件,那么用户可能是真实的界面使用者;如果被测的是一个工具模块,那么用户可能是模块调用者;更多时候,是二者的混合情况。对于真实用户场景,想想他们可能会输入什么?可能会点击什么?再想想此刻我们的组件应该作何表现。对于模块调用场景,想想调用点可能输入什么?预期获得什么样的返回?再想想我们的模块应该作何表现。
例如,上面的函数,从用户的视角,可以这样写
// 这个函数用来过滤undefined和null
function filterEmpty(arr) {
if (Array.isArray(arr)) {
return arr.filter(item => item);
} else {
return [arr].filter(item => item);
}
}
// 用例1:用户输入的数组可能包含各种基本类型的数据,其中应该只有undefined和null被过滤掉
expect(filterEmpty([0, 1, 'abc', '', undefined, null, false, true, NaN])).toEqual([0, 1, 'abc', '', false, true, NaN]);
// 用例2:输入非数组时,如果是undefined 或 null,则应被过滤掉,其它类型的值应该通过
expect(filterEmpty(undefined)).toEqual([]);
expect(filterEmpty(null)).toEqual([]);
expect(filterEmpty(0)).toEqual([0]);
// ...
始终记住:不要追求代码覆盖率,而应该追求用例覆盖率。
但很可惜,当前没有一个 用例覆盖率 统计工具,有的往往是行覆盖率、分支覆盖率,这就很容易导致我们陷入测细节的陷阱。但转变一下思维:如果从用户视角出发来写用例,最终结果应该就是100%的行覆盖率&分支覆盖率。倘若用例已经做到了100%的覆盖使用场景,而行覆盖率还没达到100%,只能说明这里面有冗余代码&分支!
如何看待TDD?
仅在当我们觉得TDD(测试驱动开发)能提升我们的效率时,才使用它。
我们在前面的讨论,都是希望单元测试能帮我们拦截代码问题,这是从防守方的视角来看待单元测试。但TDD(测试驱动开发)的观点认为,业务开发应该先写用例再写代码,通过用例去指导开发。
TDD一般包含上图的这3个步骤:
- 先写测试用例,此时用例会执行失败(红圈)。因为业务代码还没写呢
- 再写业务代码,让用例能通过(绿圈)
- 审视刚写的代码,看是否能优化重构(蓝圈)
如此往复循环,直到需求开发完成,用例也覆盖完成。
其实TDD的好处是:强迫我们必须从用户视角出发来写用例。因为在写用例时,代码还不存在呢!但是TDD循环要能顺利完成,是有前提的:
- 首先,要能根据业务需求,设计出合理的代码结构(eg:拆分哪些单元 module/class/function?它们分别承载什么功能?它们的输入输出是怎么样的?)
- 其次,根据各个单元的功能和输入输出,设计UT用例
- 第三,对代码架构和UT框架足够了解,用例失败时能迅速搞清楚,是代码的问题还是用例本身写错了
- 最后,也是最重要的,要有足够的试错时间。
所以,我的观点是:只要我们明白单元测试用例应该是从用户视角出发来写就可以了。
TDD固然是好,但前置条件也比较多,不要硬上。我们可以从简单的bugfix开始,尝试使用TDD,等我们变的熟练了,并且发现自己喜欢这个模式,再投入到需求开发。
(PS:TDD作为一种方法论,其实施效果是因人而异的,我们应该去了解它,但不应该无脑硬上。我们可以先找简单场景尝试一下,看看是否合自己的口味,再决定是否扩大使用)
单元测试框架的组成
前面的讨论中,我们直到了UT是测试系统的一环,应该在合适的地方使用它。写UT时应该瞄准用户,而不是瞄准代码细节。下面我们了解一下UT大概长什么样,UT框架大概是什么样。
一个典型的UT代码
一个前端单元测试脚本,本质还是一个js文件,只不过多了一些全局变量而已:describe、beforeAll、afterAll、beforeEach、afterEach、test、expect ...
一个单元测试脚本,写出来大概会是这种形态:
// userService.test.js - 测试套件
import { UserService } from './userService';
describe('UserService 类测试', () => {
let userService;
let testUser;
// 整个测试套件前执行一次
beforeAll(() => {
console.log('===== 启动用户服务测试 =====');
});
// 整个测试套件后执行一次
afterAll(() => {
console.log('===== 完成用户服务测试 =====');
});
// 每个测试用例前执行
beforeEach(() => {
userService = new UserService();
testUser = { id: 1, name: 'Alice', email: 'alice@example.com' };
userService.addUser(testUser);
});
// 每个测试用例后执行
afterEach(() => {
console.log(`测试完成,当前日志条目: ${userService.getLogCount()}`);
});
// BDD 风格测试组
describe('BDD 风格测试 (行为驱动开发)', () => {
test('应该正确添加用户', () => {
// BDD 断言风格
expect(userService.addUser({...})).toMatchObject({...});
});
});
// TDD 风格测试组
describe('TDD 风格测试 (测试驱动开发)', () => {
test('添加用户后用户数量应增加', () => {
// 初始状态
const initialCount = userService.users.length;
// 执行操作
userService.addUser({...});
// TDD 断言风格
expect(userService.users.length).toBe(initialCount + 1);
});
});
});
这个脚本里面,调用了被测模块UserService,组装了一些模拟数据投喂给它,然后观察它的返回结果是否符合预期,从而判断其功能是否正常。其实可以想象,最原始的方式实现一个单元测试脚本,大概是这样的:
import { UserService } from './userService';
function test() {
const userService = new UserService();
const result = userService.addUser({...});
if (isEqual(result, {...})) {
console.log('测试通过');
} else {
console.err('测试失败!');
}
}
function isEqual(obj1, obj2) {
// 判断两个对象是否值相等
}
// 执行测试
test();
但这种做法在实际使用中会遇到很多麻烦,比如:要测试DOM相关的界面操作怎么办?有很多个用例,但其中某个执行失败了怎么办?执行结果的比对可能还涉及到异步情况,怎么办?于是,UT框架出现了,它帮我们搞定了这些问题,让我们可以专心写用例。
UT框架的作用&选型
UT框架负责构建测试环境、组织测试用例、提供用例执行引擎、并搜集执行结果。
构建测试环境
UT用例往往是在Nodejs环境下执行的。但有一些被测模块或用例的执行,需要用到DOM、window、document等浏览器特有环境的资源。所以UT框架往往会提供浏览器环境的模拟能力。甚至,提供代码编译能力(例如支持ts写用例,但nodejs本身并不支持直接运行ts);甚至,提供模块打桩能力(也就是mock掉某个特定模块)。
组织测试用例
UT框架往往用describe表示一套用例,用it/test表示一个具体的用例。同时提供beforeAll、afterAll、beforeEach、afterEach等声明周期钩子。
提供用例执行引擎
真实项目中,用例数往往成百上千。这些用例以什么顺序(或并行)执行,异步用例如何执行,如果一个用例跑崩了,其它用例要能正常执行。这些也都是UT框架提供的能力。
搜集执行结果
当用例执行完成之后,整体成功了多少,失败了多少,覆盖率如何。生成不同格式的报告,来对接不同的分析工具/平台。这些也都是UT框架提供的能力。
常见前端单元测试框架对比
前端单测框架有很多种,能力各异。这里选常见的几种,就上述4个方面进行对比,如下:
一般来说,Jest是大而全且快的框架,适合大多数场景。Mocka是小而精的框架,可定制性比较强。我们可以根据自己的项目实际情况,来选择合适的测试框架。
断言库的作用&选型
相对于自己写if-else、console.log而言,断言库提供更语义化、更简洁的表达方式,并能跟UT框架协作,易于生成测试报告。
断言库的本质是一种测试专用DSL(domain-specific language领域特定语言),它让我们写出来的测试代码更易懂。它有两种语言风格:
- BDD风格:更贴近自然语言,例如 expect(user).to.be.loggedIn
- TDD风格:更贴近编程语言,例如 assert.isTrue(user.isLoggedIn)
语言风格的选择,主要是看个人合团队的喜好。选哪一种风格不重要,重要的是整个团队应该是同一种风格。
不同断言库使用的语言风格不同,但也有一些能同时支持两种风格。常见的几种断言库的特点对比如下:
写UT对开发者有什么好处?
通过写UT,我们能直观看到好/坏代码的差距,能挖掘出对业务更深的理解。
代码中的坏味道越多,我们越会感觉到UT难写。
直观体会代码坏味道
案例:一个订单处理逻辑
// 被测代码(坏味道示例)
export function processOrder(order) {
if (!order) return null;
const tax = order.items.reduce((sum, item) => {
// 深度嵌套 + 业务耦合
if (item.type === 'book') return sum + item.price * 0.1;
else if (item.type === 'food') return sum + item.price * 0.05;
}, 0);
return { ...order, tax };
}
// 测试代码(暴露问题)
test('处理空订单应返回null', () => {
expect(processOrder(null)).toBeNull(); // 通过
});
test('计算图书税率为10%', () => {
const order = { items: [{ type: 'book', price: 100 }] };
expect(processOrder(order).tax).toBe(10); // 通过
});
// 新增需求:电子产品税率15%,需修改原函数,违反开闭原则
在写测试代码的过程中,我们会发现这里有职责不单一的问题,这个函数同时包含了 处理订单和计算税率 的逻辑。而且后续如果有新增的类别(很可能发生),则要修改这个重要函数,违反开闭原则。为了解决这些问题,可以用策略模式去重构。并且用例能帮忙保证重构不引入问题。
const taxRules = {
book: price => price * 0.1,
food: price => price * 0.05,
electronics: price => price * 0.15 // 扩展不修改主函数
};
export function processOrder(order) {
if (!order) return null;
const tax = order.items.reduce(
(sum, item) => sum + (taxRules[item.type]?.(item.price) || 0),
0
);
return { ...order, tax };
}
加深对业务的理解
案例:商品税率处理逻辑
// 被测代码(坏味道示例)
export function processOrder(order) {
if (!order) return null;
const tax = order.items.reduce((sum, item) => {
// 深度嵌套 + 业务耦合
if (item.type === 'book') return sum + item.price * 0.1;
else if (item.type === 'food') return sum + item.price * 0.05;
}, 0);
return { ...order, tax };
}
// 测试用例(覆盖未考虑的场景)
test('商品类型不存在时应忽略税额', () => {
const order = { items: [{ type: 'unknown', price: 100 }] };
expect(processOrder(order).tax).toBe(0); // 原代码报错,暴露缺陷
});
在写测试代码的过程中,我们会发现代码没有处理未知的商品类型。为了修复这个问题,我们需要在原码中补一个else分支。
案例:金融计算精度问题
// 金融计算的陷阱
function calculateInterest(principal, rate, days) {
const dailyRate = rate / 365;
return principal * Math.pow(1 + dailyRate, days);
}
// 测试暴露的业务漏洞
test('10万元年化5%存30天应得409.58元利息', () => {
const interest = calculateInterest(100000, 0.05, 30);
expect(interest).toBeCloseTo(409.58, 2); // 失败!实际411.77
});
在写测试代码的过程中,我们会发现js在小数计算方面的精度能力较弱。这里需要一些辅助工具才能达到业务精度要求
总结
合理且正确的使用单元测试,能帮我们有效的提升我们对自己代码的信心,而不是产生累赘。尝试将我们的思维转型一下:
- 从 “这段代码如何实现?” → “这段代码该如何被使用?”
- 从 “它能做什么?” → “它不该做什么?”
- 从 “功能完成” → “变更安全”
这将给我们带来别样的体会。 m.ximalaya.com/sound/888156301/?6=98 m.ximalaya.com/sound/888156301/?73=2 m.ximalaya.com/sound/888156301/?08=90 m.ximalaya.com/sound/888156300/?4=34 m.ximalaya.com/sound/888156300/?12=2 m.ximalaya.com/sound/888156300/?89=67 m.ximalaya.com/sound/888156299/?5=95 m.ximalaya.com/sound/888156299/?34=1 m.ximalaya.com/sound/888156299/?67=00 m.ximalaya.com/sound/888156297/?1=64 m.ximalaya.com/sound/888156297/?45=0 m.ximalaya.com/sound/888156297/?14=98 m.ximalaya.com/sound/888156296/?3=40 m.ximalaya.com/sound/888156296/?89=3 m.ximalaya.com/sound/888156296/?98=76 m.ximalaya.com/sound/888156295/?5=10 m.ximalaya.com/sound/888156295/?67=7 m.ximalaya.com/sound/888156295/?45=67 m.ximalaya.com/sound/888156294/?4=87 m.ximalaya.com/sound/888156294/?98=8 m.ximalaya.com/sound/888156294/?01=89 m.ximalaya.com/sound/888156293/?2=10 m.ximalaya.com/sound/888156293/?78=5 m.ximalaya.com/sound/888156293/?45=45 m.ximalaya.com/sound/888156292/?8=81 m.ximalaya.com/sound/888156292/?12=3 m.ximalaya.com/sound/888156292/?45=89 m.ximalaya.com/sound/888156291/?8=42 m.ximalaya.com/sound/888156291/?78=5 m.ximalaya.com/sound/888156291/?45=78 m.ximalaya.com/sound/888156290/?5=39 m.ximalaya.com/sound/888156290/?89=4 m.ximalaya.com/sound/888156290/?55=89 m.ximalaya.com/sound/888156289/?0=39 m.ximalaya.com/sound/888156289/?87=1 m.ximalaya.com/sound/888156289/?10=47 m.ximalaya.com/sound/888156288/?1=62 m.ximalaya.com/sound/888156288/?75=1 m.ximalaya.com/sound/888156288/?89=43 m.ximalaya.com/sound/888156287/?5=42 m.ximalaya.com/sound/888156287/?67=2 m.ximalaya.com/sound/888156287/?87=55 m.ximalaya.com/sound/888156285/?3=87 m.ximalaya.com/sound/888156285/?89=7 m.ximalaya.com/sound/888156285/?97=19 m.ximalaya.com/sound/888156284/?3=39 m.ximalaya.com/sound/888156284/?74=4 m.ximalaya.com/sound/888156284/?56=23 m.ximalaya.com/sound/888156283/?7=97 m.ximalaya.com/sound/888156283/?56=5 m.ximalaya.com/sound/888156283/?90=54 m.ximalaya.com/sound/888156281/?1=62 m.ximalaya.com/sound/888156281/?99=0 m.ximalaya.com/sound/888156281/?56=01 m.ximalaya.com/sound/888156280/?1=39 m.ximalaya.com/sound/888156280/?78=5 m.ximalaya.com/sound/888156280/?57=95 m.ximalaya.com/sound/888156279/?9=93 m.ximalaya.com/sound/888156279/?01=8 m.ximalaya.com/sound/888156279/?99=32 m.ximalaya.com/sound/888156278/?3=62 m.ximalaya.com/sound/888156278/?23=3 m.ximalaya.com/sound/888156278/?39=67 m.ximalaya.com/sound/888156277/?1=17 m.ximalaya.com/sound/888156277/?78=1 m.ximalaya.com/sound/888156277/?09=23 m.ximalaya.com/sound/888156276/?7=95 m.ximalaya.com/sound/888156276/?39=2 m.ximalaya.com/sound/888156276/?90=87 m.ximalaya.com/sound/888156275/?5=59 m.ximalaya.com/sound/888156275/?34=6 m.ximalaya.com/sound/888156275/?89=86 m.ximalaya.com/sound/888156274/?8=03 m.ximalaya.com/sound/888156274/?32=5 m.ximalaya.com/sound/888156274/?34=01 m.ximalaya.com/sound/888156273/?3=97 m.ximalaya.com/sound/888156273/?01=7 m.ximalaya.com/sound/888156273/?76=23 m.ximalaya.com/sound/888156271/?2=84 m.ximalaya.com/sound/888156271/?78=8 m.ximalaya.com/sound/888156271/?01=12 m.ximalaya.com/sound/888156270/?9=54 m.ximalaya.com/sound/888156270/?12=7 m.ximalaya.com/sound/888156270/?01=43 m.ximalaya.com/sound/888156269/?0=06 m.ximalaya.com/sound/888156269/?65=1 m.ximalaya.com/sound/888156269/?51=20 m.ximalaya.com/sound/888156268/?0=53 m.ximalaya.com/sound/888156268/?09=8 m.ximalaya.com/sound/888156268/?23=00 m.ximalaya.com/sound/888156267/?8=99 m.ximalaya.com/sound/888156267/?43=0 m.ximalaya.com/sound/888156267/?89=32 m.ximalaya.com/sound/888156266/?5=39 m.ximalaya.com/sound/888156266/?34=9 m.ximalaya.com/sound/888156266/?65=90 m.ximalaya.com/sound/888156265/?6=40 m.ximalaya.com/sound/888156265/?34=4 m.ximalaya.com/sound/888156265/?00=75 m.ximalaya.com/sound/888156263/?7=87 m.ximalaya.com/sound/888156263/?78=8 m.ximalaya.com/sound/888156263/?76=45 m.ximalaya.com/sound/888156262/?5=77 m.ximalaya.com/sound/888156262/?23=6 m.ximalaya.com/sound/888156262/?89=01 m.ximalaya.com/sound/888156259/?9=48 m.ximalaya.com/sound/888156259/?45=1 m.ximalaya.com/sound/888156259/?45=45 m.ximalaya.com/sound/888156260/?1=10 m.ximalaya.com/sound/888156260/?19=7 m.ximalaya.com/sound/888156260/?12=56 m.ximalaya.com/sound/888156258/?7=06 m.ximalaya.com/sound/888156258/?20=2 m.ximalaya.com/sound/888156258/?56=90 m.ximalaya.com/sound/888156257/?6=95 m.ximalaya.com/sound/888156257/?67=4 m.ximalaya.com/sound/888156257/?20=90 m.ximalaya.com/sound/888156256/?3=76 m.ximalaya.com/sound/888156256/?01=9 m.ximalaya.com/sound/888156256/?62=90 m.ximalaya.com/sound/888156254/?8=62 m.ximalaya.com/sound/888156254/?04=5 m.ximalaya.com/sound/888156254/?97=62 m.ximalaya.com/sound/888156253/?3=93 m.ximalaya.com/sound/888156253/?34=4 m.ximalaya.com/sound/888156253/?54=06 m.ximalaya.com/sound/888156252/?7=19 m.ximalaya.com/sound/888156252/?01=3 m.ximalaya.com/sound/888156252/?01=34 m.ximalaya.com/sound/888156251/?3=26 m.ximalaya.com/sound/888156251/?43=3 m.ximalaya.com/sound/888156251/?17=31 m.ximalaya.com/sound/888156250/?4=53 m.ximalaya.com/sound/888156250/?90=8 m.ximalaya.com/sound/888156250/?19=79 m.ximalaya.com/sound/888156249/?9=84 m.ximalaya.com/sound/888156249/?89=4 m.ximalaya.com/sound/888156249/?46=23 m.ximalaya.com/sound/888156248/?2=28 m.ximalaya.com/sound/888156248/?65=8 m.ximalaya.com/sound/888156248/?19=34 m.ximalaya.com/sound/888156247/?3=40 m.ximalaya.com/sound/888156247/?65=4 m.ximalaya.com/sound/888156247/?89=90 m.ximalaya.com/sound/888156246/?8=97 m.ximalaya.com/sound/888156246/?56=9 m.ximalaya.com/sound/888156246/?12=89 m.ximalaya.com/sound/888156245/?7=93 m.ximalaya.com/sound/888156245/?55=1 m.ximalaya.com/sound/888156245/?43=67 m.ximalaya.com/sound/888156244/?9=98 m.ximalaya.com/sound/888156244/?12=1 m.ximalaya.com/sound/888156244/?90=10 m.ximalaya.com/sound/888156243/?6=53 m.ximalaya.com/sound/888156243/?01=4 m.ximalaya.com/sound/888156243/?31=24 m.ximalaya.com/sound/888156242/?8=95 m.ximalaya.com/sound/888156242/?40=3 m.ximalaya.com/sound/888156242/?86=23 m.ximalaya.com/sound/888156241/?1=53 m.ximalaya.com/sound/888156241/?43=5 m.ximalaya.com/sound/888156241/?58=56 m.ximalaya.com/sound/888156240/?0=19 m.ximalaya.com/sound/888156240/?89=4 m.ximalaya.com/sound/888156240/?09=78 m.ximalaya.com/sound/888156239/?3=73 m.ximalaya.com/sound/888156239/?12=4 m.ximalaya.com/sound/888156239/?39=42 m.ximalaya.com/sound/888156237/?6=53 m.ximalaya.com/sound/888156237/?56=7 m.ximalaya.com/sound/888156237/?65=98 m.ximalaya.com/sound/888156236/?4=43 m.ximalaya.com/sound/888156236/?78=9 m.ximalaya.com/sound/888156236/?64=05 m.ximalaya.com/sound/888156235/?7=53 m.ximalaya.com/sound/888156235/?43=1 m.ximalaya.com/sound/888156235/?44=56 m.ximalaya.com/sound/888156234/?0=23 m.ximalaya.com/sound/888156234/?78=3 m.ximalaya.com/sound/888156234/?01=12 m.ximalaya.com/sound/888156233/?0=87 m.ximalaya.com/sound/888156233/?56=5 m.ximalaya.com/sound/888156233/?67=01 m.ximalaya.com/sound/888156232/?7=73 m.ximalaya.com/sound/888156232/?12=7 m.ximalaya.com/sound/888156232/?31=89 m.ximalaya.com/sound/888156231/?8=73 m.ximalaya.com/sound/888156231/?90=3 m.ximalaya.com/sound/888156231/?56=45 m.ximalaya.com/sound/888156229/?9=62 m.ximalaya.com/sound/888156229/?34=3 m.ximalaya.com/sound/888156229/?78=67 m.ximalaya.com/sound/888156227/?8=84 m.ximalaya.com/sound/888156227/?56=6 m.ximalaya.com/sound/888156227/?78=88 m.ximalaya.com/sound/888156225/?8=40 m.ximalaya.com/sound/888156225/?42=0 m.ximalaya.com/sound/888156225/?53=98 m.ximalaya.com/sound/888156224/?1=73 m.ximalaya.com/sound/888156224/?18=7 m.ximalaya.com/sound/888156224/?87=01 m.ximalaya.com/sound/888156223/?4=64 m.ximalaya.com/sound/888156223/?77=2 m.ximalaya.com/sound/888156223/?19=23 m.ximalaya.com/sound/888156222/?0=39 m.ximalaya.com/sound/888156222/?44=6 m.ximalaya.com/sound/888156222/?45=42 m.ximalaya.com/sound/888156221/?1=60 m.ximalaya.com/sound/888156221/?31=1 m.ximalaya.com/sound/888156221/?43=90 m.ximalaya.com/sound/888156220/?9=19 m.ximalaya.com/sound/888156220/?34=4 m.ximalaya.com/sound/888156220/?67=39 m.ximalaya.com/sound/888156219/?0=39 m.ximalaya.com/sound/888156219/?32=0 m.ximalaya.com/sound/888156219/?65=01 m.ximalaya.com/sound/888156218/?3=75 m.ximalaya.com/sound/888156218/?89=3 m.ximalaya.com/sound/888156218/?45=64 m.ximalaya.com/sound/888156216/?7=08 m.ximalaya.com/sound/888156216/?53=4 m.ximalaya.com/sound/888156216/?21=40 m.ximalaya.com/sound/888156217/?8=42 m.ximalaya.com/sound/888156217/?43=1 m.ximalaya.com/sound/888156217/?10=65 m.ximalaya.com/sound/888156215/?3=21 m.ximalaya.com/sound/888156215/?31=5 m.ximalaya.com/sound/888156215/?89=78 m.ximalaya.com/sound/888156214/?7=28 m.ximalaya.com/sound/888156214/?08=3 m.ximalaya.com/sound/888156214/?87=10 m.ximalaya.com/sound/888156213/?6=64 m.ximalaya.com/sound/888156213/?08=2 m.ximalaya.com/sound/888156213/?25=53 m.ximalaya.com/sound/888156212/?0=32 m.ximalaya.com/sound/888156212/?52=0 m.ximalaya.com/sound/888156212/?23=65 m.ximalaya.com/sound/888156210/?6=28 m.ximalaya.com/sound/888156210/?01=2 m.ximalaya.com/sound/888156210/?23=19 m.ximalaya.com/sound/888156209/?0=40 m.ximalaya.com/sound/888156209/?45=6 m.ximalaya.com/sound/888156209/?12=89 m.ximalaya.com/sound/888156208/?1=60 m.ximalaya.com/sound/888156208/?31=9 m.ximalaya.com/sound/888156208/?76=78 m.ximalaya.com/sound/888156207/?8=65 m.ximalaya.com/sound/888156207/?78=6 m.ximalaya.com/sound/888156207/?31=56 m.ximalaya.com/sound/888156206/?5=96 m.ximalaya.com/sound/888156206/?98=4 m.ximalaya.com/sound/888156206/?12=56 m.ximalaya.com/sound/888156204/?8=45 m.ximalaya.com/sound/888156204/?97=4 m.ximalaya.com/sound/888156204/?01=34 m.ximalaya.com/sound/888156203/?7=51 m.ximalaya.com/sound/888156203/?20=6 m.ximalaya.com/sound/888156203/?89=89 m.ximalaya.com/sound/888156201/?3=37 m.ximalaya.com/sound/888156201/?10=4 m.ximalaya.com/sound/888156201/?56=97 m.ximalaya.com/sound/888156200/?2=26 m.ximalaya.com/sound/888156200/?67=3 m.ximalaya.com/sound/888156200/?53=28 m.ximalaya.com/sound/888156199/?4=86 m.ximalaya.com/sound/888156199/?54=6 m.ximalaya.com/sound/888156199/?86=67 m.ximalaya.com/sound/888156198/?6=81 m.ximalaya.com/sound/888156198/?40=1 m.ximalaya.com/sound/888156198/?23=67 m.ximalaya.com/sound/888156197/?0=34 m.ximalaya.com/sound/888156197/?45=6 m.ximalaya.com/sound/888156197/?23=78 m.ximalaya.com/sound/888156196/?3=39 m.ximalaya.com/sound/888156196/?12=1 m.ximalaya.com/sound/888156196/?34=23 m.ximalaya.com/sound/888156194/?1=08 m.ximalaya.com/sound/888156194/?01=7 m.ximalaya.com/sound/888156194/?34=01 m.ximalaya.com/sound/888156193/?1=42 m.ximalaya.com/sound/888156193/?78=6 m.ximalaya.com/sound/888156193/?34=32 m.ximalaya.com/sound/888156192/?9=97 m.ximalaya.com/sound/888156192/?60=0 m.ximalaya.com/sound/888156192/?31=53 m.ximalaya.com/sound/888156191/?3=94 m.ximalaya.com/sound/888156191/?89=3 m.ximalaya.com/sound/888156191/?34=43 m.ximalaya.com/sound/888156190/?8=15 m.ximalaya.com/sound/888156190/?45=1 m.ximalaya.com/sound/888156190/?86=89 m.ximalaya.com/sound/888156189/?4=84 m.ximalaya.com/sound/888156189/?89=3 m.ximalaya.com/sound/888156189/?43=20 m.ximalaya.com/sound/888156188/?5=87 m.ximalaya.com/sound/888156188/?73=1 m.ximalaya.com/sound/888156188/?51=56 m.ximalaya.com/sound/888156187/?2=74 m.ximalaya.com/sound/888156187/?56=8 m.ximalaya.com/sound/888156187/?28=08 m.ximalaya.com/sound/888156185/?8=98 m.ximalaya.com/sound/888156185/?09=7 m.ximalaya.com/sound/888156185/?73=23 m.ximalaya.com/sound/888156184/?9=53 m.ximalaya.com/sound/888156184/?22=8 m.ximalaya.com/sound/888156184/?90=90 m.ximalaya.com/sound/888156183/?9=28 m.ximalaya.com/sound/888156183/?69=7 m.ximalaya.com/sound/888156183/?78=79 m.ximalaya.com/sound/888156179/?7=39 m.ximalaya.com/sound/888156179/?23=8 m.ximalaya.com/sound/888156179/?86=56 m.ximalaya.com/sound/888156180/?8=09 m.ximalaya.com/sound/888156180/?23=9 m.ximalaya.com/sound/888156180/?67=45 m.ximalaya.com/sound/888156178/?4=10 m.ximalaya.com/sound/888156178/?65=5 m.ximalaya.com/sound/888156178/?78=64 m.ximalaya.com/sound/888156177/?6=73 m.ximalaya.com/sound/888156177/?01=9 m.ximalaya.com/sound/888156177/?97=56 m.ximalaya.com/sound/888156175/?0=04 m.ximalaya.com/sound/888156175/?97=5 m.ximalaya.com/sound/888156175/?56=45 m.ximalaya.com/sound/888156176/?4=73 m.ximalaya.com/sound/888156176/?56=3 m.ximalaya.com/sound/888156176/?23=89 m.ximalaya.com/sound/888156174/?7=40 m.ximalaya.com/sound/888156174/?73=3 m.ximalaya.com/sound/888156174/?78=70 m.ximalaya.com/sound/888156173/?3=09 m.ximalaya.com/sound/888156173/?53=9 m.ximalaya.com/sound/888156173/?36=54 m.ximalaya.com/sound/888156171/?6=73 m.ximalaya.com/sound/888156171/?23=6 m.ximalaya.com/sound/888156171/?34=89 m.ximalaya.com/sound/888156172/?6=10 m.ximalaya.com/sound/888156172/?64=5 m.ximalaya.com/sound/888156172/?23=87 m.ximalaya.com/sound/888156170/?8=28 m.ximalaya.com/sound/888156170/?01=7 m.ximalaya.com/sound/888156170/?12=56 m.ximalaya.com/sound/888156169/?0=84 m.ximalaya.com/sound/888156169/?67=0 m.ximalaya.com/sound/888156169/?23=17 m.ximalaya.com/sound/888156167/?7=95 m.ximalaya.com/sound/888156167/?67=1 m.ximalaya.com/sound/888156167/?06=45 m.ximalaya.com/sound/888156166/?2=87 m.ximalaya.com/sound/888156166/?64=2 m.ximalaya.com/sound/888156166/?45=01 m.ximalaya.com/sound/888156165/?8=65 m.ximalaya.com/sound/888156165/?90=4 m.ximalaya.com/sound/888156165/?66=89 m.ximalaya.com/sound/888156163/?8=51 m.ximalaya.com/sound/888156163/?12=6 m.ximalaya.com/sound/888156163/?90=34 m.ximalaya.com/sound/888156162/?1=10 m.ximalaya.com/sound/888156162/?09=7 m.ximalaya.com/sound/888156162/?43=87 m.ximalaya.com/sound/888156161/?0=73 m.ximalaya.com/sound/888156161/?01=8 m.ximalaya.com/sound/888156161/?23=54 m.ximalaya.com/sound/888156160/?2=89 m.ximalaya.com/sound/888156160/?34=7 m.ximalaya.com/sound/888156160/?34=09 m.ximalaya.com/sound/888156158/?5=98 m.ximalaya.com/sound/888156158/?34=6 m.ximalaya.com/sound/888156158/?53=90 m.ximalaya.com/sound/888156157/?4=77 m.ximalaya.com/sound/888156157/?78=1 m.ximalaya.com/sound/888156157/?89=78 m.ximalaya.com/sound/888156156/?4=31 m.ximalaya.com/sound/888156156/?31=1 m.ximalaya.com/sound/888156156/?53=12 m.ximalaya.com/sound/888156155/?4=98 m.ximalaya.com/sound/888156155/?12=6 m.ximalaya.com/sound/888156155/?20=56 m.ximalaya.com/sound/888156154/?7=20 m.ximalaya.com/sound/888156154/?90=4 m.ximalaya.com/sound/888156154/?45=20 m.ximalaya.com/sound/888156153/?1=32 m.ximalaya.com/sound/888156153/?34=5 m.ximalaya.com/sound/888156153/?42=23 m.ximalaya.com/sound/888156152/?1=02 m.ximalaya.com/sound/888156152/?45=9 m.ximalaya.com/sound/888156152/?03=90