【问题标题】:How to Test Uniqueness Constraint with Ecto如何使用 Ecto 测试唯一性约束
【发布时间】:2016-09-11 19:42:48
【问题描述】:

我正在尝试测试我的数据库中的唯一性,但遇到了一些麻烦。我运行了这个迁移:

def change do
  create table(:signups) do
    add :name, :string
    add :email, :string

    timestamps()
  end

  create unique_index(:signups, [:email])
end

在我的模型中有这个变更集定义:

def changeset(struct, params \\ %{}) do
  struct
  |> cast(params, [:name, :email])
  |> validate_required([:name, :email])
  |> validate_format(:email, ~r/@/)
  |> update_change(:email, &String.downcase/1)
  |> unique_constraint(:email)
end

失败的测试是:

test "duplicate email changeset is invalid" do
  %Signup{}
  |> Signup.changeset(@valid_attrs)
  |> Repo.insert!

  user2 = %Signup{}
  |> Signup.changeset(@valid_attrs)
  assert {:error, _changeset} = Repo.insert(user2)
end

第二个插入似乎通过了,即使它不应该通过。返回的确切错误是:

1) test duplicate email changeset is invalid (EventSignup.SignupTest)
     test/models/signup_test.exs:24
     match (=) failed
     code: {:error, _changeset} = Repo.insert(user2)
     rhs:  {:ok,
            %EventSignup.Signup{__meta__: #Ecto.Schema.Metadata<:loaded, "signups">,
             email: "some@content.com", id: 41,
             inserted_at: #Ecto.DateTime<2016-09-11 19:35:40>,
             name: "some content",
             updated_at: #Ecto.DateTime<2016-09-11 19:35:40>}}
     stacktrace:
       test/models/signup_test.exs:31: (test)

有人看到我在这里缺少什么吗?如果我通过 iex 手动插入两条相同的记录,第二次插入将失败,但在测试期间它通过了。

【问题讨论】:

  • 你确定这个约束存在于测试数据库中吗?也许您在添加约束之前迁移了测试数据库?尝试再次删除、创建和迁移测试数据库 (MIX_ENV=test mix do ecto.drop, ecto.create, ecto.migrate),然后运行测试。
  • @Dogbert 你是对的。这个错误来自我不知道有一个单独的测试数据库,并且标准混合 ecto.rollback、混合 ecto.migrate 不会涵盖在以后向迁移添加唯一约束。学过的知识。你是第一个,所以如果你把这个作为答案,我很乐意接受。

标签: elixir phoenix-framework ecto


【解决方案1】:

我认为您的迁移和模型是正确的。但是,当您应该执行以下操作时,您似乎在断言宏上使用 =:

test "duplicate email changeset is invalid" do
  %Signup{}
  |> Signup.changeset(@valid_attrs)
  |> Repo.insert!

  user2 = %Signup{}
  |> Signup.changeset(@valid_attrs)

  {:error, changeset} = Repo.insert(user2)
  refute changeset.valid?
end

这是我的测试工作。

test "only one setting per company" do
  first_setting = insert(:setting)
  second_setting = params_for(:setting, %{company_id: first_setting.company.id})
  changeset = Setting.changeset(%Setting{}, second_setting)
  {:error, changeset} = Repo.insert changeset
  assert changeset.errors == [company_id: {"has already been taken", []}]
  refute changeset.valid?
end

insert 是来自 ex-machina 的辅助方法,它创建一个有效的模型,并且参数返回一个具有相同 company_id 的有效地图以强制错误发生。

【讨论】:

    【解决方案2】:

    在测试环境MIX_ENV=test iex -S mix运行iex,尝试插入两条值相同的记录。如果它允许您这样做,则意味着您的测试数据库缺少 unique_index。 重要的!记得之后清理你的测试数据库。

    或者:
    跳过上面的描述并运行MIX_ENV=test mix ecto.reset,然后再次运行您的测试。如果他们通过了您的测试,则 db 缺少 unique_index。

    【讨论】:

    • btw ecto.reset 是 [ecto.drop, ecto.create, ecto.migrate] 的别名
    • 帮我解决了!我的印象是数据库在每次测试运行时都会运行最新的迁移,但事实并非如此。
    猜你喜欢
    • 1970-01-01
    • 2018-03-10
    • 1970-01-01
    • 2019-09-23
    • 1970-01-01
    • 2018-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多