LoginSignup
11
3

【Elixir】Mockを使ってみる

Posted at

はじめに

AtCoderのプログラムは標準入力からデータを読み込み、結果を標準出力に出力するプログラムを作ります。
これをLivebookで実行するにはどうすればよいか。
この方法を模索していていました。
テストで利用するMockを利用する事で、実現できました。この方法を書いてみます。

やりたいこと

AtCoderの回答のプログラムでは、IOモジュールを使うと想定します。
IO.put, IO.read, IO.getn の第一引数はdeviceで:stdioが渡されます。この値を別の値に置き換えて、実行すること。

IOモジュールを置き換える方法

次の記事でモック化について、そのバックグランドや、いくつかの方法が紹介されているので、一度みてみると参考になるかと思います。
https://blog.appsignal.com/2023/04/11/an-introduction-to-mocking-tools-for-elixir.html

今回は、この記事でも最初に紹介されている、Mockを使いました。

記述方法

Mockはユニットテストで使う事が多いので、testの中での記述例が多いですが、今回は、AtCoderのプログラムを実行するという目的なので、Judgeモジュールの中に実装してみました。
with_mock関数がポイントです。
ここにMock化するモジュールと関数名、置き換える関数を与えて、その中で対象の処理(今回は、Main.main())を実行します。

judge(string_in, string_out) は入力例、出力例を文字列で渡すとMain.main()を実行して、結果の判定をする関数です。

defmodule Judge do
  import Mock

  def judge(string_in, string_out) do
    {:ok, file} = StringIO.open(string_in)

    with_mock IO, [:passthrough], [
      getn: fn :stdio, a, b -> passthrough([file, a, b]) end,
      getn: fn a, b -> passthrough([file, a, b]) end,
      read: fn :stdio, option -> passthrough([file, option]) end,
      read: fn option -> passthrough([file, option]) end,
      puts: fn :stdio, item -> passthrough([file, item]) end,
      puts: fn item -> passthrough([file, item]) end,
    ] do
      Main.main()
    end

    {:ok, {_, result}} = StringIO.close(file)

    if string_out == result do
      IO.puts("OK")
      true
    else
      IO.inspect(string_in, label: "Input")
      IO.inspect(string_out, label: "Expected")
      IO.inspect(result, label: "Received")
      false
    end
  end
end

ハマりポイント

IO.read(:stdio,:line)をMockにして、その内部で、IO.read(file,:line)を呼び出す処理が必要です。
この時、IO.readはモック化されているので、そのまま記述すると、定義されていないエラーになってしまいます。
Mockにはpassthough機能があったので、解決できました。

まとめ

  • Mockを使うことで、簡単な記述で、任意のモジュールを入れ替えた状態で実行する事ができる。
  • passthough機能もある
  • AtCoderの回答を修正することなくLivebook上で実行できた。
11
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
3