$ mix hex.info
Hex: 0.17.1
Elixir: 1.5.1
OTP: 20.1
Built with: Elixir 1.5.1 and OTP 18.3
MariaDB 10.2.9
I’m writing some tests for my User
schema:
defmodule Myflow.Wechat.User do
use Ecto.Schema
import Ecto.Changeset
alias Myflow.Wechat.User
schema "wechat_users" do
field :open_id, :string
field :timezone, :string, default: "Asia/Shanghai"
timestamps()
end
@doc false
def changeset(%User{} = user, attrs) do
user
|> cast(attrs, [:open_id, :timezone])
|> validate_required([:open_id])
|> unique_constraint(:open_id)
end
end
Unique index is already created in migration:
defmodule Myflow.Repo.Migrations.CreateWechatUsers do
use Ecto.Migration
def change do
create table(:wechat_users) do
add :open_id, :string
add :timezone, :string
timestamps(size: 6)
end
create unique_index(:wechat_users, [:open_id])
end
end
And here’s my test:
defmodule Myflow.Wechat.UserTest do
use Myflow.DataCase
alias Myflow.Wechat.User
alias Myflow.Repo
@valid_attrs %{open_id: "xxx"}
@invalid_attrs %{}
test "open_id should not be duplicated" do
changeset = User.changeset(%User{}, @valid_attrs)
assert {:ok, changeset} = Repo.insert changeset
assert {:error, changeset} = Repo.insert changeset
end
end
When I run mix test
, I got:
1) test open_id should not be duplicated (Myflow.Wechat.UserTest)
test/myflow/wechat/user_test.exs:10
** (Ecto.ConstraintError) constraint error when attempting to insert struct:
* unique: PRIMARY
If you would like to convert this constraint into an error, please
call unique_constraint/3 in your changeset and define the proper
constraint name. The changeset has not defined any constraint.
code: assert {:error, changeset} = Repo.insert changeset
stacktrace:
(ecto) lib/ecto/repo/schema.ex:570: anonymous fn/4 in Ecto.Repo.Schema.constraints_to_errors/3
(elixir) lib/enum.ex:1255: Enum."-map/2-lists^map/1-0-"/2
(ecto) lib/ecto/repo/schema.ex:555: Ecto.Repo.Schema.constraints_to_errors/3
(ecto) lib/ecto/repo/schema.ex:219: anonymous fn/14 in Ecto.Repo.Schema.do_insert/4
test/myflow/wechat/user_test.exs:13: (test)
The weird part is, if I run Repo.insert changeset
twice in iex
,
Myflow.Repo.insert u
[debug] QUERY OK db=5.8ms
INSERT INTO `wechat_users` (`open_id`,`timezone`,`inserted_at`,`updated_at`) VALUES (?,?,?,?) ["xxx", "Asia/Shanghai", {{2017, 11, 3}, {1, 2, 14, 214822}}, {{2017, 11, 3}, {1, 2, 14, 214829}}]
{:ok,
%Myflow.Wechat.User{__meta__: #Ecto.Schema.Metadata<:loaded, "wechat_users">,
id: 6, inserted_at: ~N[2017-11-03 01:02:14.214822], open_id: "xxx",
timezone: "Asia/Shanghai", updated_at: ~N[2017-11-03 01:02:14.214829]}}
(search)`r': Myflow.Repo.insert u
[debug] QUERY ERROR db=2.3ms
INSERT INTO `wechat_users` (`open_id`,`timezone`,`inserted_at`,`updated_at`) VALUES (?,?,?,?) ["xxx", "Asia/Shanghai", {{2017, 11, 3}, {1, 2, 22, 790847}}, {{2017, 11, 3}, {1, 2, 22, 790853}}]
{:error,
#Ecto.Changeset<action: :insert,
changes: %{open_id: "xxx", timezone: "Asia/Shanghai"},
errors: [open_id: {"has already been taken", []}],
data: #Myflow.Wechat.User<>, valid?: false>}
Everything works as I expected.
Just don’t know what could be wrong with my test code here. Any help?