npm package: @lugia/jverify
Description: 用于单元测试对象或方法打桩
Author: yss
Latest version: 1.0.12

#模块说明
用于单元测试过程中，对对象或者方法进行打桩出来。
#兼容说明
兼容浏览器IE9以上版本
#安装
npm install @lugia/jverify
#普通函数打桩

```
const { mockFunction } = require('@lugia/jverify');
const { create } = mockFunction;

const mock = create();
// 获取mock后的目标方法
const targetFunc = mock.getFunction();
mock.mock(() => 1);
// 直接指定返回值,PS：returned的优先级高于mock指定的返回值
// 如本例子，虽然指定了mock(()=>1)，但是会先将returned的返回值处理完成后
// 使用mock方法
mock.returned(100);
// 可指定多次返回值
mock.returned(101);

console.info(targetFunc(1));
console.info(targetFunc.bind({ t: 'a' })(2));
console.info(targetFunc.bind({ t: 'b' })(2));

console.info('方法调用次数', mock.callTimes());
console.info('方法调用的入参', mock.queryCallArgs());
console.info('方法调用绑定的this', mock.queryCallContext());
// 永远返回某个值
mock.forever('forever');
console.info('永远返回:', targetFunc(1));
console.info('永远返回:', targetFunc(1));
console.info('永远返回:', targetFunc(1));
// 重置
mock.restore();
console.info('方法调用次数', mock.callTimes());
console.info('方法调用的入参', mock.queryCallArgs());
console.info('方法调用绑定的this', mock.queryCallContext());
```
#对象打桩
##mock对象的方法
```
const { create } = mockObject;
const target = {
  name: 'old name',
  getName () {
    return this.name;
  },
};


const mockObjectTarget = create(target);

console.info('获取mock操作对象');
const mockFunction = mockObjectTarget.mockFunction('getName');

console.info(target.getName());
console.info('默认this 是对象本身');
console.info('方法调用绑定的this', target.getName());
mockFunction.queryCallContext();

console.info('改变原始方法绑定的this');
mockFunction.mockContext({ name: 'I am mock orgial!' });

console.info(target.getName());

console.info('指定mock方法的this为 {t: \'I am Mock\'}');
mockFunction.mock(() => 'mock jun', { t: 'I am Mock' });
console.info(`直接指定返回值,PS：returned的优先级高于mock指定的返回值
如本例子，虽然指定了mock(()=>1)，但是会先将returned的返回值处理完成后
 使用mock方法`);
mockFunction.returned('liming');
console.info('可指定多次返回值');
mockFunction.returned('xiaohong');

console.info(target.getName('a'));
console.info(target.getName('b'));
console.info(target.getName('c', 'd'));

console.log('方法调用次数', mockFunction.callTimes());
console.info('方法调用的入参', mockFunction.queryCallArgs());
console.info('方法调用绑定的this', mockFunction.queryCallContext());
// 永远返回某个值
mockFunction.forever('forever');
console.info('永远返回:', target.getName('a'));
console.info('永远返回:', target.getName('b'));
console.info('永远返回:', target.getName('c', 'd'));

console.info('重置mock对象的状态');
mockFunction.restore();
console.info('方法调用次数', mockFunction.callTimes());
console.info('方法调用的入参', mockFunction.queryCallArgs());
console.info('方法调用绑定的this', mockFunction.queryCallContext());

```
##mock对象的属性
```
const { mockObject } = require('@lugia/jverify');
const { create } = mockObject;
const target = {
  name: 'old name',
};


const mockObjectTarget = create(target);

console.info('获取mock操作对象');
const mockVar = mockObjectTarget.mockVar('name');

console.info(target.name);

mockVar.mock(() => 'mock jun');
console.info(`直接指定返回值,PS：returned的优先级高于mock指定的返回值
如本例子，虽然指定了mock(()=>1)，但是会先将returned的返回值处理完成后
 使用mock方法`);
mockVar.returned('liming');
console.info('可指定多次返回值');
mockVar.returned('xiaohong');

console.info(target.name);
console.info(target.name);
console.info(target.name);

console.log('方法调用次数', mockVar.callTimes());
// 永远返回某个值
mockVar.forever('forever');
console.info('永远返回:', target.name);
console.info('永远返回:', target.name);
console.info('永远返回:', target.name);

console.info('重置mock对象的状态');
mockVar.restore();
console.info('方法调用次数', mockVar.callTimes());

```
##restoreAll & restore
mockObject下mockVar、mockFunction对象，支持restore方法用于还原为原有处理。
mockFunction也支持restore方法用于还原原有处理。
为了方便处理，mockObject还提供了restoreAll用来还原所有的mockFunction与mockVar.

##VerifyOrder
用于进行mock对象之间顺序，以及方法mock对象的入参、this引用的检查。并给出响应错误信息的提示。
注意：当第3个步骤出错后，不会再去检查第3步骤后的相关步骤的具体错误。因为，实际开发过程中，也是对错误的不够逐一进行调整。
```
const { VerifyOrder, mockFunction, mockObject } = require('@lugia/jverify');
// mock 的目标对象
const obj = {
  f1 () {
  },
  v1: '1',
};
// 调用顺序验证器
const order = VerifyOrder.create();

// mock 对象
const mockObj = mockObject.create(obj, { mockName: 'a', verifyOrder: order });
// 模拟 方法：f1() 和 属性:v1
mockObj.mockFunction('f1');
mockObj.mockVar('v1');
// mock 一个普通方法
const mockFunc = mockFunction.create({ mockName: 'f1', verifyOrder: order });
const targetFunc = mockFunc.getFunction();
const ctx = { ctx: 'ctx' };
// 实际调用
targetFunc.call(ctx, 'hello');
obj.f1();
obj.v1;
obj.f1();
obj.v1;


// 期望调用顺序 正确调用顺序
order.verify(obj => {
  const { a, f1 } = obj;
  f1('hello').withContext(ctx);
  a.f1();
  a.v1;
  a.f1();
  a.v1;
});

try {

  // 上下未正确
  order.verify(obj => {
    const { a, f1 } = obj;
    f1('hello').withContext({});
    a.f1();
    a.v1;
    a.f1();
    a.v1;
  });
} catch (err) {
  console.error(err);
}

try {

  // 不正确的调用顺序
  order.verify(obj => {
    const { a, f1 } = obj;
    f1('hello');
    a.f1();
    a.v1;
    a.f1();
    a.v1;
    a.v1;
    a.v1;
  });
} catch (err) {
  console.error(err);
}

```
##reset & resetAll
mock对象方法的时候，存在一种情况。比如我们需要在nodejs环境下对Promise进行mock操作，可以按如下代码编写：
```
  const { mockObject } = require('@lugia/jverify');
  const globalMock = mockObject.create(global);
  globalMock.mockFunction('Promise').mock((resolve, reject)=>{
  });  
```
但是此时，即使使用restore进行还原也无法将Promise.all的方法还原回来，此时就可以通过reset或者mock对象中的resetAll方法进行还原成最原始的方法。