我目前正在使用Karma测试运行器来进行我的Angular项目,使用茉莉花测试框架。它工作得很好,但我有一个问题:当一个对象比较失败时,生成的打印到控制台真的很难读,并且这些对象拥有的属性越来越难。例如:
Expected spy spy to have been called with [ { currentCareMoment : { ID : 5, Description : 'Late namiddag (16-20)', StartHour : 16, EndHour : 20 }, previousCareMoment : { ID : 4, Description : 'Namiddag (14-16)', StartHour : 14, EndHour : 16 } } ] but actual calls were [ { currentCareMoment : { ID : 6, Description : 'Avond (20-24)', StartHour : 20, EndHour : 24 }, previousCareMoment : { ID : 5, Description : 'Late namiddag (16-20)', StartHour : 16, EndHour : 20 } } ].
有没有设置Jasmine(因为我认为Karma与它无关)打印更漂亮的物体?只是一些换行和缩进已经是一个巨大的帮助。例如:
Expected spy spy to have been called with [ {
currentCareMoment : {
ID : 5,
Description : 'Late namiddag (16-20)',
StartHour : 16,
EndHour : 20
},
previousCareMoment : {
ID : 4,
Description : 'Namiddag (14-16)',
StartHour : 14,
EndHour : 16
}
} ] but actual calls were [ {
currentCareMoment : {
ID : 6,
Description : 'Avond (20-24)',
StartHour : 20,
EndHour : 24
},
previousCareMoment : {
ID : 5,
Description : 'Late namiddag (16-20)',
StartHour : 16,
EndHour : 20
}
} ].
答案 0 :(得分:13)
我的回答是基于茉莉花2.0.1。
方法1记录在jasmine docs中。所以可能会推荐。
但方法2要简单得多。
我最初的目的是创建一个自定义匹配器,如here所述。所以我从jasmine源代码中复制了toHaveBeenCalledWith匹配器并对其进行了修改,以便打印出来:
var matchers = {
toBeCalledWith: function (util, customEqualityTesters) {
return {
compare: function() {
var args = Array.prototype.slice.call(arguments, 0),
actual = args[0],
expectedArgs = args.slice(1),
result = { pass: false };
if (!jasmine.isSpy(actual)) {
throw new Error('Expected a spy, but got ' + jasmine.JSON.stringify(actual, undefined, 2) + '.');
}
if (!actual.calls.any()) {
result.message = function() {
return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + JSON.stringify(expectedArgs, undefined, 2) + ' but it was never called.';
};
return result;
}
if (util.contains(actual.calls.allArgs(), expectedArgs, customEqualityTesters)) {
result.pass = true;
result.message = function() {
return 'Expected spy ' + actual.and.identity() + ' not to have been called with ' + JSON.stringify(expectedArgs, undefined, 2) + ' but it was.';
};
} else {
result.message = function() {
return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + JSON.stringify(expectedArgs, undefined, 2) + ' but actual calls were ' + JSON.stringify(actual.calls.allArgs(), undefined, 2) + '.';
};
}
return result;
}
};
}
};
测试用例将使用我们的新匹配器:
describe('Test', function() {
beforeEach(function() {
jasmine.addMatchers(matchers);
});
it('should print pretty', function() {
var spy = jasmine.createSpy('spy');
spy({
currentCareMoment: {
ID: 5,
Description: 'Late namiddag (16-20)',
StartHour: 16,
EndHour: 20
},
previousCareMoment: {
ID: 4,
Description: 'Namiddag (14-16)',
StartHour: 14,
EndHour: 16
}});
expect(spy).toBeCalledWith({
currentCareMoment: {
ID: 6,
Description: 'Avond (20-24)',
StartHour: 20,
EndHour: 24
},
previousCareMoment: {
ID: 5,
Description: 'Late namiddag (16-20)',
StartHour: 16,
EndHour: 20
}
});
});
});
jasmine.pp
但是,在实现这一过程中,我注意到jasmine使用函数jasmine.pp
进行漂亮的打印。所以我想我可以通过在我的测试文件上添加以下内容来覆盖它:
jasmine.pp = function(obj) {
return JSON.stringify(obj, undefined, 2);
};
答案 1 :(得分:7)
由于添加了其他答案的时间,karma-jasmine-diff-reporter中提供了漂亮的打印选项。我建议尝试一下 - 它非常易于配置,并且与其他常见的测试记者一起为我工作很好。
最小配置如下:
reporters: ['jasmine-diff'],
jasmineDiffReporter: {
multiline: true,
pretty: true
},
答案 2 :(得分:5)
我发现覆盖jasmine.pp
导致我的规范记者不再对实际与预期的差异进行颜色编码。
我的解决方案是将以下代码段添加到它自己的文件中,将其加载到karma.conf中,然后将自定义匹配器(使用underscore用于断言深度相等)添加到生成颜色的报告器的配置中 - 控制台中的编码差异(karma-jasmine-diff-reporter)
//This will run before all of our specs because it's outside of a describe block
beforeEach(function() {
var objectMatcher = {
toEqualObject: function(util, customEqualityTesters) {
return {
compare: function(actual, expected) {
var result = {};
result.pass = _.isEqual(actual, expected);
if (result.pass) {
result.message = "Expected \n" + JSON.stringify(actual, null, 2) + "\n not to equal \n" + JSON.stringify(expected, null, 2) + "\n";
} else {
result.message = "Expected \n" + JSON.stringify(actual, null, 2) + "\n to equal \n" + JSON.stringify(expected, null, 2) + "";
}
return result;
}
};
}
};
jasmine.addMatchers(objectMatcher);
});
现在我可以通过调用expect(foo).toEqualObject(bar)
:
弄清楚如何使用茉莉花间谍进行这项工作留给读者练习。
答案 3 :(得分:0)
使用
JSON.stringify(obj, undefined, 2)
第3个参数是缩进级别
答案 4 :(得分:0)
这是一个茉莉花自定义匹配器,它从与“ expected”中的Any术语匹配的结果中删除调用参数。 Node的util.inspect
用于处理循环引用和漂亮的打印结果。
输出:
custom matcher
1) should not display arguments corresponding to expected Any arguments
0 passing (15ms)
1 failing
1) custom matcher
should not display arguments corresponding to expected Any arguments:
ExpectationFailed: Expected spy underTest to have been called with [
Any { expectedObject: [Function: Object] },
{ name: 'Bob' },
{ name: 'New York' },
Any { expectedObject: [Function: Object] }
] but actual calls were [
[
'<Hidden due to Any match>',
{ name: 'Joe' },
{ name: 'New York' },
'<Hidden due to Any match>'
]
].
toHaveBeenCalledWith.js
'use strict'
import { inspect } from 'util'
export function toHaveBeenCalledWith2 (util, customEqualityTesters) {
return {
compare: function () {
const args = Array.prototype.slice.call(arguments, 0)
const actual = args[0]
const expectedArgs = args.slice(1)
const result = { pass: false }
if (!isSpyLike(actual)) {
throw new Error('Expected a spy, but got ' + pp(actual) + '.')
}
if (!actual.calls.any()) {
result.message = function () { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + pp(expectedArgs) + ' but it was never called.'; }
return result
}
if (util.contains(actual.calls.allArgs(), expectedArgs, customEqualityTesters)) {
result.pass = true
result.message = function () { return 'Expected spy ' + actual.and.identity() + ' not to have been called with ' + pp(expectedArgs) + ' but it was.' }
} else {
const anyIndexes = getIndexesOfJasmineAnyArgs(expectedArgs)
const actualArgs = stripJasmineAnyArgsFromActual(actual.calls.allArgs(), anyIndexes)
result.message = function () { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + pp(expectedArgs) + ' but actual calls were ' + pp(actualArgs) + '.' }
}
return result
},
}
}
function stripJasmineAnyArgsFromActual (actualArgsList, indexesToIgnore) {
const strippedArgs = []
actualArgsList.forEach(args => {
const stripped = args.map((arg, argIndex) => {
if (indexesToIgnore.includes(argIndex)) {
return '<Hidden due to Any match>'
} else {
return arg
}
})
strippedArgs.push(stripped)
})
return strippedArgs
}
function getIndexesOfJasmineAnyArgs (expectedArgs) {
const anyIndexes = []
expectedArgs.forEach((arg, i) => {
if (arg.constructor.name === 'Any') {
anyIndexes.push(i)
}
})
return anyIndexes
}
function isSpyLike (possibleSpy) {
if (!possibleSpy) {
return false
}
return possibleSpy.and && possibleSpy.and.constructor.name === 'SpyStrategy' &&
possibleSpy.calls && possibleSpy.calls.constructor.name === 'CallTracker'
}
function pp (args) {
return inspect(args, { depth: 5 })
}
用法:
import { toHaveBeenCalledWith2 } from '../matchers/toHaveBeenCalledWith'
describe('custom matcher', function () {
beforeEach(async function () {
const matchers = {
toHaveBeenCalledWith2,
}
jasmine.addMatchers(matchers)
})
it('should not display arguments corresponding to expected Any arguments', function () {
const obj = {
underTest: function (a, person, city, d) { },
}
const expectedPerson = {
name: 'Bob',
}
const expectedCity = {
name: 'New York',
}
spyOn(obj, 'underTest')
obj.underTest({}, { name: 'Joe' }, { name: 'New York' }, {})
expect(obj.underTest).toHaveBeenCalledWith2(
jasmine.any(Object),
expectedPerson,
expectedCity,
jasmine.any(Object),
)
})
})