上一章里,我們用 mix phx.gen.html
命令創(chuàng)建出完整用戶界面,并且具備增加、刪除、更改、查詢用戶的功能。
這一章,我們將實現(xiàn) username
的第一個規(guī)則:username
必填,如果未填寫,提示用戶請?zhí)顚?/code>。
先來看看,在 http://localhost:4000/users/new
頁面上,提交空白用戶名的話,我們會看到什么?
頁面會提示我們,can't be blank
。
很好,雖然不知道怎么回事,但必填的限制已經有了,那如何將它改成請?zhí)顚?/code>呢?
打開 web/models/user.ex
文件,其中有一行:
|> validate_required([:username, :email, :password])
正是 validate_required
明確了 username
為必填。從文檔里我們看到,validate_required
還接收一個可選的 message
參數,用于自定義錯誤消息。
讓我們加上試試:
diff --git a/tv_recipe/users/user.ex b/tv_recipe/users/user.ex
index b7713a0..87ce321 100644
--- a/web/models/user.ex
+++ b/web/models/user.ex
@@ -15,7 +15,7 @@ defmodule TvRecipe.User do
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:username, :email, :password])
- |> validate_required([:username, :email, :password])
+ |> validate_required([:username, :email, :password], message: "請?zhí)顚?)
|> unique_constraint(:username)
|> unique_constraint(:email)
end
打開網址,提交空白用戶名,頁面上已經顯示“請?zhí)顚憽绷耍?/p>
很好,但請注意,我們這是人肉測試。
又或者,我們可以用 Phoenix 生成的測試文件來驗證。
打開 test/tv_recipe/users_test.exs
文件,默認內容如下:
defmodule TvRecipe.UserTest do
use TvRecipe.ModelCase
alias TvRecipe.User
@valid_attrs %{email: "some content", password: "some content", username: "some content"}
@invalid_attrs %{}
test "changeset with valid attributes" do
changeset = User.changeset(%User{}, @valid_attrs)
assert changeset.valid?
end
test "changeset with invalid attributes" do
changeset = User.changeset(%User{}, @invalid_attrs)
refute changeset.valid?
end
end
文件中有兩個變量,@valid_attrs
表示有效的 User
屬性,@invalid_attrs
表示無效的 User
屬性,我們按本章開頭擬定的規(guī)則修改 @valid_attrs
:
diff --git a/test/tv_recipe/users_test.exs b/test/tv_recipe/users_test.exs
index 1d5494f..7c73207 100644
--- a/test/tv_recipe/users_test.exs
+++ b/test/tv_recipe/users_test.exs
@@ -3,7 +3,7 @@ defmodule TvRecipe.UserTest do
alias TvRecipe.User
- @valid_attrs %{email: "some content", password: "some content", username: "some content"}
+ @valid_attrs %{email: "chenxsan@gmail.com", password: "some content", username: "chenxsan"}
@invalid_attrs %{}
test "changeset with valid attributes" do
接著,在 users_test.exs
文件中添加一個新測試:
diff --git a/test/models/user_test.exs b/test/models/user_test.exs
index 7c73207..4c174ab 100644
--- a/test/tv_recipe/users_test.exs
+++ b/test/tv_recipe/users_test.exs
@@ -15,4 +15,9 @@ defmodule TvRecipe.UserTest do
changeset = User.changeset(%User{}, @invalid_attrs)
refute changeset.valid?
end
+
+ test "username should not be blank" do
+ attrs = %{@valid_attrs | username: ""}
+ assert %{username: ["請?zhí)顚?] } = errors_on(%User{}, attrs)
+ end
end
這里,%{@valid_attrs | username: ""}
是 Elixir 更新映射(Map)的一個方法。
至于 errors_on/2
函數,它需要新增在 test/support/data_case.ex
文件中:
def errors_on(changeset) do
Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
Regex.replace(~r"%{(\w+)}", message, fn _, key ->
opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
end)
end)
end
+
+ def errors_on(struct, attrs) do
+ changeset = struct.__struct__.changeset(struct, attrs)
+ errors_on(changeset)
+ end
end
是否很吃驚?要知道,如果是在 JavaScript 里寫兩個同名函數,后一個函數會覆蓋前一個的定義,而 Elixir 下,我們可以定義多個同名函數,它們能處理不同的狀況,而又互不干擾。
它檢查給定數據中的錯誤消息,并返回給我們。
現(xiàn)在在命令行下運行:
$ mix test test/tv_recipe/users_test.exs
結果如下:
...
Finished in 0.07 seconds
3 tests, 0 failures
測試通過?,F(xiàn)在我們可以放心地認為,用戶提交空白 username
時,Phoenix 一定會返回“請?zhí)顚憽钡腻e誤消息。
為什么要寫測試?
可能很多人都抱有這個疑問。測試增加了我們的工作量,而它的作用又不那么明顯。更何況,這還只是一個入門教程。
我想談幾點個人感受:
- 我不喜歡拿自己當人肉測試機。
- 代碼的修改是必然發(fā)生的,而我們在修改時無法保證周全,此時測試即任務清單,它幫我們指出,哪些地方的代碼需統(tǒng)一修改,這樣我們才能保證代碼的質量。
- 在團隊協(xié)作中,你很難保證別人的代碼不會破壞到自己的那部分。比如一個開源項目,有人在 github 上提了 pull request,你如果有測試代碼,馬上就能知道,這個 pull request 是否會破壞其它功能,如果你沒有測試代碼,好了,你得一行一行驗證了 - 這種成本無論是對維護者還是貢獻者來說,都是極大的浪費。
而況在 Phoenix 框架下,測試非常容易寫。
更多建議: