【The Art of Unit Testing 3_自学笔记06】3.4 + 3.5 单元测试核心技能之:函数式注入与模块化注入的解决方案简介-3.4 函数式依赖注入技术 Functional injection techniques

函数式实现(FP)与面向对象实现(OOP)并无绝对的优劣之分。FP 固然简洁、清晰、自证性强,但学习曲线陡峭也是不争的事实。

上一节讲到断开外部依赖的一种方案——参数注入法。它通过重构原函数,使其接收一个新参数值(即人为控制的星期索引值)。但这里的参数除了基本类型外,还可以将具体星期值的计算逻辑封装到一个函数内,然后将该函数以参数的形式注入原函数。

于是有了函数式注入的第一套方案——函数作参数注入。对原函数模块 password-verifier-time00.js 作如下更改(L2、L3):

const SUNDAY = 0, SATURDAY = 6;
const verifyPassword3 = (input, rules, getDayFn) => {
  const dayOfWeek = getDayFn();
  if ([SATURDAY, SUNDAY].includes(dayOfWeek)) {
    throw Error("It's the weekend!");
  }
  // more code goes here...
  // return list of errors found..
  return [];
};

于是单元测试 password-verifier-time00.spec.js 相应变为(L4、L5,以及 L9、L11):

const SUNDAY = 0, SATURDAY = 6, MONDAY = 2;
describe('verifier3 - dummy function', () => {
  test('on weekends, throws exceptions', () => {
    const alwaysSunday = () => SUNDAY;
    expect(() => verifyPassword3('anything', [], alwaysSunday))
      .toThrowError("It's the weekend!");
  });
  test('on week days, works fine', () => {
    const alwaysMonday = () => MONDAY;

    const result = verifyPassword3('anything', [], alwaysMonday);

    expect(result.length).toBe(0);
  });
});

实测结果:

图 5 改为函数作参数后的实测结果

【图 5 改为函数作参数后的实测结果】

再进一步,可将传入的函数改造为一个高阶函数(high order function,简称 HOF),让依赖注入逻辑与密码校验逻辑分开。这样就有了书中所说的 工厂函数(factory functions) 方案。此时原函数已经被完全改造了。

password-verifier-time00.js

const SUNDAY = 0, SATURDAY = 6;

const makeVerifier = (rules, dayOfWeekFn) => {
  return function (input) {
    if ([SATURDAY, SUNDAY].includes(dayOfWeekFn())) {
      throw new Error("It's the weekend!");
    }
    const errors = [];
    // more code goes here..
    return errors;
  };
};

module.exports = {
  makeVerifier
};

于是单元测试 password-verifier-time00.spec.js 也要同步更新:

const { makeVerifier } = require('../password-verifier-time00');
const SUNDAY = 0, MONDAY = 1;

describe('verifier3 - dummy function', () => {
  test('factory method: on weekends, throws exceptions', () => {
    const alwaysSunday = () => SUNDAY;
    const verifyPassword = makeVerifier([], alwaysSunday);

    expect(() => verifyPassword('anything'))
      .toThrow("It's the weekend!");
  });

  test('on week days, works fine', () => {
    const alwaysMonday = () => MONDAY;
    const verifyPassword = makeVerifier([], alwaysMonday);

    const result = verifyPassword('anything');

    expect(result.length).toBe(0);
  });

});

实测结果同上面的 图 5。这样做的好处,就是让校验的配置独立于校验的执行,在减少原函数参数个数的同时,测试用例的可读性也更强。一举多得。

上一篇:spark集群模式-standalone的配置和使用


下一篇:ubuntu的ftp的问题