[英]fp-ts and jest: ergonomic tests for Option and Either?
我正在使用fp-ts
,並使用 Jest 編寫單元測試。 在許多情況下,我正在測試可為空的結果,通常用Option
或Either
表示(通常,數組find
s)。 如果結果是無(以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
的類型。 在許多情況下,必須編寫它是繁重的,需要編寫和導出接口僅用於測試目的(這也不符合人體工程學)。
有沒有辦法簡化這種測試?
編輯:這是一個更接近真實條件的測試用例:
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',
});
});
});
toNullable
或toUndefined
怎么toUndefined
? 給定Option<string>
, toNullable
返回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");
});
expect(Option.isSome(result)).toEqual(true)
是,類型保護isSome
不能用於縮小expect
外部代碼路徑中的result
(請參閱此處控制流分析的工作原理)。
您可以使用更精簡的斷言函數並將它們與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');
});
我不知道是否有一種用類型保護簽名來增強 Jest expect
函數類型本身的好方法,但我懷疑它是否簡化了您的案例,而不是簡單的斷言或以上解決方案。
你可以像這樣從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
});
這個問題是在這一點上有點歷史,擁有一個公認的答案,但也有使測試夫婦非常好的圖書館fp-ts
Either
和Option
真正愉快。 他們都工作得很好,我真的無法決定我更喜歡哪個。
它們允許你寫這樣的東西:
test('some test', () => {
expect(E.left({ code: 'invalid' })).toSubsetEqualLeft({ code: 'invalid' })
})
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.