简体   繁体   English

fp-ts 和jest:Option 和Either 的人体工程学测试?

[英]fp-ts and jest: ergonomic tests for Option and Either?

I'm using fp-ts , and I write unit tests with Jest.我正在使用fp-ts ,并使用 Jest 编写单元测试。 In many cases, I'm testing nullable results, often represented with Option or Either (typically, array find s).在许多情况下,我正在测试可为空的结果,通常用OptionEither表示(通常,数组find s)。 What is the most ergonomic way to make the test fail if the result is none (taking Option as an example), and keep on going knowing that this result is some?如果结果是无(以Option为例),并且继续知道这个结果是一些,那么使测试失败的最符合人体工程学的方法是什么?

Here's an example of how I can solve the problem at the moment:这是我目前如何解决问题的示例:

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');
  });
});

But having to write an if with an early return is not very ergonomic.但是必须写一个提前返回的 if 不是很符合人体工程学。

I could alternatively write an as assertion:我也可以写一个as断言:

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

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

But the downside there is that I have to rewrite the some 's type.但缺点是我必须重写some的类型。 In many cases, having to write it is heavy, requiring to write and export interface for the sole purpose of testing (which is not ergonomic either).在许多情况下,必须编写它是繁重的,需要编写和导出接口仅用于测试目的(这也不符合人体工程学)。

Is there any way to simplify this kind of test?有没有办法简化这种测试?

Edit: Here is a test case that's closer to real conditions:编辑:这是一个更接近真实条件的测试用例:


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',
    });
  });
});

How about toNullable or toUndefined ? toNullabletoUndefined怎么toUndefined Given Option<string> , toNullable returns string | null给定Option<string>toNullable返回string | null string | null . string | null

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

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

The problem with expect(Option.isSome(result)).toEqual(true) is, type guard isSome cannot be used to narrow result in the outer code path of expect (see here how control flow analysis works). expect(Option.isSome(result)).toEqual(true)是,类型保护isSome不能用于缩小expect外部代码路径中的result (请参阅此处控制流分析的工作原理)。

You can use assertion functions which are a bit leaner and combine them with fp-ts type guards, eg:您可以使用更精简的断言函数并将它们与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');
});

I don't know if there is a good way of augmenting Jest expect function type itself with a type guard signature, but I doubt it simplifies your case instead of a simple assertion or above solutions.我不知道是否有一种用类型保护签名来增强 Jest expect函数类型本身的好方法,但我怀疑它是否简化了您的案例,而不是简单的断言或以上解决方案。

You can write an unsafe conversion fromSome like so:你可以像这样从fromSome写一个不安全的转换:

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

And then use it in the tests然后在测试中使用它

  it(`should do something with a "some" result`, () => {
    const result = someFunc('abcd');
    const myValue = fromSome(result);
    // do something with myValue
  });

This question is a little old at this point and has an accepted answer, but there are couple really nice libraries that make testing fp-ts Either and Option really pleasant.这个问题是在这一点上有点历史,拥有一个公认的答案,但也有使测试夫妇非常好的图书馆fp-ts EitherOption真正愉快。 They both work really well and I can't really decide which I like better.他们都工作得很好,我真的无法决定我更喜欢哪个。

They allow you to write things like this:它们允许你写这样的东西:

test('some test', () => {
  expect(E.left({ code: 'invalid' })).toSubsetEqualLeft({ code: 'invalid' })
})

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM