1

我正在使用fp-ts,我用 Jest 编写单元测试。在许多情况下,我正在测试可以为空的结果,通常用Optionor表示Either(通常是 array finds)。如果结果为无(以Option示例为例),那么使测试失败的最符合人体工程学的方法是什么,并继续知道这个结果是一些?

这是我目前如何解决问题的示例:

function someFunc(input: string): Option.Option<string> {
  return Option.some(input);
}

describe(`Some suite`, () => {
  it(`should do something with a "some" result`, () => {
    const result = someFunc('abcd');

    // This is a fail case, here I'm expecting result to be Some
    if(Option.isNone(result)) {
      expect(Option.isSome(result)).toEqual(true);
      return;
    }

    expect(result.value).toEqual('abcd');
  });
});

但是必须写一个 if 并提前返回不是很符合人体工程学。

我也可以写一个as断言:

  // ...
  it(`should do something with a "some" result`, () => {
    const result = someFunc('abcd') as Option.Some<string>;

    expect(result.value).toEqual('abcd');
  });
  // ...

但缺点是我必须重写some's 类型。在许多情况下,必须编写它很繁重,需要编写和导出接口仅用于测试目的(这也不符合人体工程学)。

有没有办法简化这种测试?

编辑:这是一个更接近真实条件的测试用例:


interface SomeComplexType {
  id: string,
  inputAsArray: string[],
  input: string;
}

function someFunc(input: string): Option.Option<SomeComplexType> {
  return Option.some({
    id: '5',
    inputAsArray: input.split(''),
    input,
  });
}

describe(`Some suite`, () => {
  it(`should do something with a "some" result`, () => {
    const result = someFunc('abcd');

    // This is the un-ergonomic step
    if(Option.isNone(result)) {
      expect(Option.isSome(result)).toEqual(true);
      return;
    }

    // Ideally, I would only need this:
    expect(Option.isSome(result)).toEqual(true);
    // Since nothing will be ran after it if the result is not "some"
    // But I can imagine it's unlikely that TS could figure that out from Jest expects

    // Since I now have the value's actual type, I can do a lot with it
    // I don't have to check it for nullability, and I don't have to write its type
    const myValue = result.value;

    expect(myValue.inputAsArray).toEqual(expect.arrayContaining(['a', 'b', 'c', 'd']));

    const someOtherThing = getTheOtherThing(myValue.id);

    expect(someOtherThing).toMatchObject({
      another: 'thing',
    });
  });
});
4

3 回答 3

2

toNullable或者怎么样toUndefined?给定Option<string>toNullable回报string | null

import { toNullable, toUndefined } from "fp-ts/lib/Option";

it(`should do something with a "some" result`, () => {
  expect(toNullable(someFunc("abcd"))).toEqual("abcd");
});

问题expect(Option.isSome(result)).toEqual(true)是,类型保护isSome不能用于缩小result外部代码路径expect(请参阅此处控制流分析的工作原理)。

您可以使用更精简的断言函数并将它们与 fp-ts类型保护结合起来,例如:

import { isSome, Option } from "fp-ts/lib/Option"

function assert<T>(guard: (o: any) => o is T, o: any): asserts o is T {
  if (!guard(o)) throw new Error() // or add param for custom error
}

it(`a test`, () => {
  const result: Option<string> = {...}
  assert(isSome, result)
  // result is narrowed to type "Some" here
  expect(result.value).toEqual('abcd');
});

我不知道是否有一种expect使用类型保护签名来增强 Jest 函数类型本身的好方法,但我怀疑它是否简化了您的案例,而不是简单的断言或上述解决方案。

于 2020-01-07T16:36:55.733 回答
2

您可以像这样编写不安全的转换fromSome

function fromSome<T>(input: Option.Option<T>): T {
  if (Option.isNone(input)) {
    throw new Error();
  }
  return input.value;
}

然后在测试中使用它

  it(`should do something with a "some" result`, () => {
    const result = someFunc('abcd');
    const myValue = fromSome(result);
    // do something with myValue
  });
于 2020-01-08T08:08:03.397 回答
0

这个问题在这一点上有点老了,并且有一个公认的答案,但是有几个非常好的库可以使测试变得fp-ts Either非常Option愉快。它们都工作得很好,我真的不能决定我更喜欢哪个。

它们允许您编写如下内容:

test('some test', () => {
  expect(E.left({ code: 'invalid' })).toSubsetEqualLeft({ code: 'invalid' })
})
于 2021-06-17T21:38:13.813 回答