前のエントリでEither/TaskEitherを使ったエラー処理について書きました。
ある関数を実行した時、「実行が成功したか」と「実行結果」が欲しい場合がよくあります。
C#の時には、タプルを使って (bool:isSuc...
指定したファイルが存在すればその内容を返して、存在しなければErrorを返す関数です。
const readFile = (fileName: string):TE.TaskEither<Error,string> => TE.tryCatch( // 正常時に返すもの async () => { return await fs.readFile(fileName, 'utf-8') }, // 異常時に返すもの (e) => { if(e instanceof Error) { return e } else { return new Error('failed') } } )
この関数を利用しているユースケース関数をテストしたいと思った時、テスト環境(=指定したファイルが存在するかどうか)がテストの成否に影響してしまうため、モック化したいと思います。
正常系のモックは、無条件でファイルが存在したことにして、その内容を返却する関数を作ればいいことになります。
すると、TaskEither
const readFileMockOK = (fileName: string):TE.TaskEither<Error,string> => { return Promise.resolve(E.right("OK, This is Dummy Contents")) }
これはコンパイルエラーになり動きません。
ここで、Either/Task/TaskEitherの定義に立ち返ってみます。
まずEitherです。
type Either<E, A> = Left<E> | Right<A>
LeftとRightのunion型を持つ型となります
次にTaskです
interface Task<A> { (): Promise<A> }
引数無しで、A型をPromiseで返す「関数」となります。
そしてTaskEitherは、Eitherを上記TaskのA型に入れたものになります。
interface TaskEither<E, A> extends Task<Either<E, A>> {}
なので、TaskEither型を返すモックは、
const readFileMockOK = (fileName: string):TE.TaskEither<Error,string> => () => Promise.resolve(E.right("OK, This is Dummy Contents")) const readFileMockNG = (fileName: string):TE.TaskEither<Error,string> => () => Promise.resolve(E.left(new Error("Dummy Error")))
と書けます。
実はこれはTaskEitherのright/left関数として既に用意されているので、下記のように簡単に書けます。
const readFileMockOK = (fileName: string):TE.TaskEither<Error,string> => TE.right("OK, This is Dummy Contents") const readFileMockNG = (fileName: string):TE.TaskEither<Error,string> => TE.left(new Error("Dummy Error"))