10
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ElixirAdvent Calendar 2023

Day 20

ElixirでTDD②- Phoenixでクエリパラメータを足し算して画面に表示する

Last updated at Posted at 2023-12-24

こんにちは!
プログラミング未経験文系出身、Elixirの国に迷い込んだ?!見習いアルケミストのaliceと申します。
今回はElixirでTDDの演習として、Phoenixでクエリパラメータを渡して足し算する方法をまとめます。

目的

TDDの演習としてPhoenixでクエリパラメータを引数a, bとし、 a + b の実行結果を画面に表示する実装をする。

実行環境

Windows 11 + WSL2 + Ubuntu 22.04
Elixir v1.14.3
Erlang v26.0.2
Phoenix v1.7.10

前提

本シリーズは下記で生成されたPhoenixのプロジェクトを使用しています。

bash
mix phx.new dec19
cd dec19
mix ecto.create

事前準備

mix test.watchの導入

TDDではテストが通る/通らないを継続的に監視する必要があるので、まずmix test.watchを導入します。
詳しくは下記で記事化しています。
以降はmix test.watchの動作確認まで済んでいる前提で進めます。

ロジック部分の実装

テストを書く

前回の記事と同様にまず、テストを書きます。

lib/dec19.ex
 @doc """
 Get the result of adding and display.

   ## Examples
       iex> Dec19.display(1, 2)
       "add result: 3"
   """
 end

(適当な実装を書いて)テストが落ちることを確認する

次に、a + bを実行するロジック部分の実装を適当に書きます。

lib/dec19.ex
   @doc """
   Get the result of adding and display.

   ## Examples
       iex> Dec19.display(1, 2)
       "add result: 3"
   """
+  def display(a, b) do
+    nil
+  end

Redの状態であることを確認します。

bash
Running tests...
Compiling 1 file (.ex)
.....

  1) doctest Dec19.display/2 (3) (Dec19Test)
     test/dec19_test.exs:3
     Doctest failed
     doctest:
       iex> Dec19.display(1, 2)
       "add result: 3"
     code:  Dec19.display(1, 2) === "add result: 3"
     left:  nil
     right: "add result: 3"
     stacktrace:
       lib/dec19.ex:20: Dec19 (module)


Finished in 0.08 seconds (0.03s async, 0.05s sync)
5 tests, 3 doctests, 1 failure

落ちたテストを通す最低限の実装をする

最後に、落ちたテストを通す最低限の実装をします。

lib/dec19.ex
   @doc """
   Get the result of adding and display.

   ## Examples
       iex> Dec19.display(1, 2)
       "add result: 3"
   """
   def display(a, b) do
-    nil
+    "add result: 3"
   end
 end

Greenの状態であることを確認します。1

bash
Running tests...
Compiling 1 file (.ex)
......
Finished in 0.09 seconds (0.03s async, 0.06s sync)
5 tests, 3 doctests, 0 failures

ロジック部分のリファクタリング

テストを書く

こちらも前回の記事と同様にdoctestを増やしてみます。

lib/dec19.ex
   @doc """
   Get the result of adding and display.

   ## Examples
       iex> Dec19.display(1, 2)
       "add result: 3"
       
+      iex> Dec19.display(2, 3)
+     "add result: 5"
   """
   def display(a, b) do
     "add result: 3"
   end
 end

テストが落ちることを確認する

再びRedの状態であることを確認します。

bash
Running tests...
Compiling 1 file (.ex)
.......

  1) doctest Dec19.display/2 (4) (Dec19Test)
     test/dec19_test.exs:3
     Doctest failed
     doctest:
       iex> Dec19.display(2, 3)
       "add result: 5"
     code:  Dec19.display(2, 3) === "add result: 5"
     left:  "add result: 3"
     right: "add result: 5"
     stacktrace:
       lib/dec19.ex:23: Dec19 (module)

..
Finished in 0.08 seconds (0.03s async, 0.05s sync)
5 tests, 4 doctests, 1 failure

落ちたテストを通す最低限の実装をする

落ちたテストを通す最低限の実装をします。
このとき、他のテストが落ちないように初めてa + bという変数を用いた実装になります。

lib/dec19.ex
 defmodule Dec19 do
   @doc """
   Get the result of adding and display.

   ## Examples
       iex> Dec19.display(1, 2)
       "add result: 3"
       
       iex> Dec19.display(2, 3)
      "add result: 5"
   """
   def display(a, b) do
-    "add result: 3"
+    "add result: #{a + b}"
   end
 end

再び、Greenの状態であることを確認します。

bash
Running tests...
Compiling 1 file (.ex)
.........
Finished in 0.08 seconds (0.03s async, 0.05s sync)
5 tests, 4 doctests, 0 failures

1つの実装で2つのテストを通すリファクタリングができました。

画面表示部分の実装

リテラルで引数a, bを渡す

home.html.heex内でロジック部分で実装した関数displayを呼び出します。
ひとまずリテラルで引数a, bをそれぞれ1, 3として渡します。2

lib/dec19_web/controllers/page_html/home.html.heex
+ <%= Dec19.display(1, 3)%>
  <.flash_group flash={@flash} />
#後略

image.png

引数a, bを変数化する

下記の仕様を叶える実装を作っていきます。

  • URLがhttp://localhost:4000/?a=1&b=2だとして、引数a, b がそれぞれ1, 2になるようにしたい。
  • 画面にはadd result: 3が出てほしい。

paramsが使えるようにする

クエリパラメータ(params)を使えるように下記の通り書き換えます。

lib/dec19_web/controllers/page_controller.ex
 defmodule Dec19Web.PageController do
   use Dec19Web, :controller

-   def home(conn, _params) do
+   def home(conn, params) do
     # The home page is often custom made,
     # so skip the default app layout.
-   render(conn, :home, layout: false)
+   render(conn, :home, layout: false, params: params)
   end
 end

heexファイルでparamsを使う

下記のように書き換えてparamsをロジック側に渡します。

lib/dec19_web/controllers/page_html/home.html.heex
- <%= Dec19.display(1, 3)%>
+ <%= Dec19.display(@params["a"], @params["b"])%>
  <.flash_group flash={@flash} />
#後略

もともとあったテストが落ちたので修正

もともとあったpage_controller_test.exsのテストがクエリパラメータが無い場合にnil + nilを足そうとして落ちました。

bash
Running tests...
Compiling 1 file (.ex)
....
  1) test GET / (Dec19Web.PageControllerTest)
     test/dec19_web/controllers/page_controller_test.exs:4
     ** (ArithmeticError) bad argument in arithmetic expression: nil + nil
     code: conn = get(conn, ~p"/")
     stacktrace:
       :erlang.+(nil, nil)
       #後略

heexファイル側でクエリパラメータの有/無で条件分岐を書き、クエリパラメータが有るときだけparamsをロジック側に渡すようにします。

lib/dec19_web/controllers/page_html/home.html.heex
- <%= Dec19.display(@params["a"], @params["b"])%>
+ <%= if @params["a"] != nil && @params["b"] != nil, do: Dec19.display(@params["a"], @params["b"]), else: "" %>
  <.flash_group flash={@flash} />
#後略

条件分岐を書いた結果、page_controller_test.exsのテストは通るようになりましたがパラメータが文字列として渡されているようです。

bash
** (exit) an exception was raised:
    ** (ArithmeticError) bad argument in arithmetic expression
        :erlang.+("1", "2")

文字列ではなく数値として渡すように修正します。

lib/dec19_web/controllers/page_html/home.html.heex
- <%= if @params["a"] != nil && @params["b"] != nil, do: Dec19.display(@params["a"], @params["b"]), else: "" %>
+ <%= if @params["a"] != nil && @params["b"] != nil, do: Dec19.display(String.to_integer(@params["a"]), String.to_integer(@params["b"])), else: "" %>

  <.flash_group flash={@flash} />
#後略

エラーも出なくなり、かつテストも全件通るようになりました。

bash
Running tests...
Compiling 1 file (.ex)
.........
Finished in 0.06 seconds (0.02s async, 0.04s sync)
5 tests, 4 doctests, 0 failures

画面側で動作確認

1件目http://localhost:4000/?a=1&b=2の場合
image.png

bash
[info] GET /
[debug] Processing with Dec19Web.PageController.home/2
  Parameters: %{"a" => "1", "b" => "2"}
  Pipelines: [:browser]
[info] Sent 200 in 2ms

2件目http://localhost:4000/?a=1&b=5の場合(念の為)
image.png

bash
[info] GET /
[debug] Processing with Dec19Web.PageController.home/2
  Parameters: %{"a" => "1", "b" => "5"}
  Pipelines: [:browser]
[info] Sent 200 in 2ms

コンソール上でもパラメータが渡されているのが確認でき、クエリパラメータに応じて画面の表示が変化することが確認できました(^▽^)/

リポジトリ

不明点

・引数a, bを変数化する背景(Phoenixのライフサイクルそのもの?)が分かっていない
・パラメータの渡し方(@params["a"]といった書き方)が分かっていない
・paramsってクエリパラメータ以外にも使うことなかったっけ?

~Elixirの国のご案内~

↓Elixirって何ぞや?と思ったらこちらもどぞ。Elixirは先端のアレコレをだいたい全部できちゃいます:laughing::sparkles::sparkles:

↓ゼロからElixirを始めるなら「エリクサーチ」がおすすめ!私もエンジニア未経験から学習中です。

We Are The Alchemists, my friends!:bouquet:3
Elixirコミュニティは本当に優しくて温かい人たちばかり!
私が挫折せずにいられるのもこの恵まれた環境のおかげです。
まずは気軽にコミュニティを訪れてみてください。4

  1. Red → Greenに状態が変わったのでマイルストーンとしてgit commitしておきます。

  2. リテラルで画面上は表示が正しくできていることが大事。ここまで作れていればdisplay関数の出力結果を使用した後続の関数処理や画面の組み立てができる!

  3. @torifukukaiouさんのAwesomeな名言をお借りしました。Elixirコミュニティを一言で表すと、これに尽きます。

  4. @kn339264さんの素敵なスライドをお借りしました。Elixirコミュニティはいろんな形で活動中!

10
0
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
10
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?