importassertfrom'assert';constHopeTests:[string,()=>void][]=[];letHopePass=0;letHopeFail=0;letHopeError=0;// Record a single test for running later.
consthopeThat=(message:string,callback:()=>void)=>{HopeTests.push([message,callback]);}constmain=()=>{HopeTests.forEach(([message,test])=>{try{test();HopePass+=1;}catch(e){if(einstanceofassert.AssertionError){HopeFail+=1;}else{HopeError+=1;}}});console.log(`pass ${HopePass}`);console.log(`fail ${HopeFail}`);console.log(`error ${HopeError}`);}
// Something to test(doesn't handle zero properly)
constsign=(value:number)=>{if(value<0){return-1;}else{return1;}}// These two should pass
hopeThat('Sign of negative is -1',()=>assert(sign(-3)===-1));hopeThat('Sign of positive is 1',()=>assert(sign(10)===1));// This one should fail.
hopeThat('Sign of zero is 0',()=>assert(sign(0)===0));// This one is an error.
hopeThat('Sign mispelled is erorr',()=>assert(sign(sgn(1)===1)));// Call the main driver
main()
importminimistfrom'minimist';import{glob}from'glob';importhopefrom'./hope';import{fileURLToPath}from'url';constparse=(args:string[])=>{constparsed=minimist(args)return{// Default root directory is current directory if not specified
root:parsed.root||'.',// Output format can be 'terse' or 'verbose' (default)
output:parsed.output||'verbose',// Array of test filenames if explicitly provided
filenames:parsed._||[]}}constmain=async(args:Array<string>)=>{constoptions=parse(args);if(options.filenames.length==0){options.filenames=awaitglob(`${options.root}/**/test*.{ts,js}`);}for(constfofoptions.filenames){constabsolutePath=fileURLToPath(newURL(f,import.meta.url));awaitimport(absolutePath);}hope.run()constresult=(options.output==='terse')?hope.terse():hope.verbose();console.log(result);}main(process.argv.slice(2))
/**
* assert 抛出指定的异常
*/exportfunctionassertThrows<TextendsError>(expectedType:new(...args:any[])=>T,func:()=>void){try{// expected to throw exception
func();// unreachable
assert(false,`Expected function to throw ${expectedType.name} but it did not throw`);}catch(error){assert(errorinstanceofexpectedType,`Expected function to throw ${expectedType.name} but it threw ${errorinstanceofError?error.constructor.name:typeoferror}`);}}/**
* assert 两个元素相等
*/exportfunctionassertEqual<T>(actual:T,expected:T,message:string){assert(actual===expected,message);}/**
* assert 两个 Set 相同
*/exportfunctionassertSetEqual<T>(actual:Set<T>,expected:Set<T>,message:string){assert(actual.size==expected.size,message);for(constelementofactual){assert(expected.has(element),message);}}/**
* assert 两个 Map 相同
*/exportfunctionassertMapEqual<Kextendsstring|number|symbol,V>(actual:Record<K,V>,expected:Record<K,V>,message:string){constactualKeys=Object.keys(actual)asK[];constexpectedKeys=Object.keys(expected)asK[];assert(actualKeys.length===expectedKeys.length,message);for(constactualKeyofactualKeys){assert(expected[actualKey]&&actual[actualKey]==expected[actualKey],message);}}/**
* assert两个列举的值相等,如元素相等,但是顺序不同也被视为相同
*/exportfunctionassertArraySame<T>(actual:Array<T>,expected:Array<T>,message:string){assert(actual.length===expected.length,message);assertSetEqual(newSet(actual),newSet(expected),message);}
importassertfrom"assert";importhope,{assertArraySame,assertMapEqual,assertSetEqual,assertThrows}from"./hope";hope.test('test assertSetEqual happy path',()=>{constsetA=newSet([1,2,3,4,5]);constsetB=newSet([5,1,2,4,3]);assertSetEqual(setA,setB,'Set supposed to be equal');assertSetEqual(newSet([]),newSet([]),'Empty Set');});hope.test('test assertMapEqual unhappy path',()=>{assertThrows(assert.AssertionError,()=>{constsetA=newSet([1,2,3,4,5]);constsetB=newSet([1,2,4,3]);assertSetEqual(setA,setB,'Set supposed to be equal');})});hope.test('test assertMapEqual happy path',()=>{constmapA={'a':1,'b':2,};constmapB={'b':2,'a':1};assertMapEqual(mapA,mapB,'Map supposed to be map');});hope.test('test assertMapEqual unhappy path',()=>{constmapA={'a':1,'b':3};constmapB={'b':2,'a':1};assertThrows(assert.AssertionError,()=>{assertMapEqual(mapA,mapB,'Map supposed to be map');});});hope.test('test assertArraySame happy path',()=>{constarr1=[1,2,3,2];constarr2=[2,1,2,3];assertArraySame(arr1,arr2,"Arrays should have same elements");// Passe
});hope.test('test assertArraySame unhappy path',()=>{constarr1=[1,2,3,2];constarr2=[2,1,2,4];assertThrows(assert.AssertionError,()=>{assertArraySame(arr1,arr2,"Arrays should have same elements");// Passe
});});
对于 hope.test 函数,我们还可以提供一个额外的参数,用于给这个test case 打标签:
1
2
3
hope.test('Difference of 1 and 2',()=>assert((1-2)===-1),['math','fast'])
然后通过 -t/--tag 按指定的tag来运行测试用例, 实现起来很容易:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
test(comment:string,callback:()=>void,tags:Array<string>=[]){this.todo.push([`${caller()}::${comment}`,callback,tags]);}run(tag:string=''){this.todo.filter(([comment,test,tags])=>{if(tag.length===0){returntrue;}returntags.indexOf(tag)>-1;}).forEach(([comment,test,tags])=>{// run the test, nothing change
})}
importhope,{assertEqual}from"./hope";letx=0;constcreateFixtures=()=>{x=1;}hope.setup(createFixtures);hope.test('Validate x should be 1',()=>{assertEqual(x,1,'X should be 1');});constcleanUp=()=>{x=0;}hope.teardown(cleanUp);