Last active
July 28, 2020 21:43
-
-
Save TylerPachal/dcc2632edb7b8525052daee69d9cec45 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule EctoValidationsTest do | |
@moduledoc """ | |
I set up these tests to document how some of the built-in Ecto Changeset | |
validations work. It seems as though: | |
- If a field is marked as required, no subsequent validation will be done | |
when the field is not present in the params. | |
- Duplicating validate_required checks does not add duplicate errors, but | |
duplicating other types of validations does add duplicate errors. | |
- If a field passes its validate_required check, then all subsequent | |
validation checks will be run, even if some fail (no early exiting). | |
- If changes are applying applied to an existing struct, the existing | |
values are only used for satisfying the validate_required check. If | |
there is invalid data already in the struct, none of the validation | |
checks will complain and the invalid data will persist. | |
""" | |
use ExUnit.Case | |
alias Ecto.Changeset | |
defmodule Person do | |
defstruct [:name, :age] | |
end | |
@types %{name: :string, age: :integer} | |
describe "on empty struct" do | |
test "when a field is missing, no further validation occurs on that field" do | |
params = %{"name" => "tyler"} | |
changeset = | |
{%Person{}, @types} | |
|> Changeset.cast(params, Map.keys(@types)) | |
|> Changeset.validate_required([:name, :age]) | |
|> Changeset.validate_inclusion(:age, 0..100) # This will not be run for this case | |
|> Changeset.validate_inclusion(:age, 13..19) # This will not be run for this case | |
assert changeset.valid? == false | |
assert changeset.errors == [ | |
{:age, {"can't be blank", [validation: :required]}} | |
] | |
end | |
test "when a field is present, the next validations are all run, even if a previous one fails" do | |
params = %{"name" => "tyler", "age" => -1} | |
changeset = | |
{%Person{}, @types} | |
|> Changeset.cast(params, Map.keys(@types)) | |
|> Changeset.validate_required([:name, :age]) | |
|> Changeset.validate_inclusion(:age, 0..100) | |
|> Changeset.validate_exclusion(:age, [-1]) | |
assert changeset.valid? == false | |
assert changeset.errors == [ | |
{:age, {"is reserved", [{:validation, :exclusion}, {:enum, [-1]}]}}, | |
{:age, {"is invalid", [{:validation, :inclusion}, {:enum, 0..100}]}} | |
] | |
end | |
test "duplicate validate_required checks do not add duplicate errors" do | |
params = %{"name" => "tyler"} | |
changeset = | |
{%Person{}, @types} | |
|> Changeset.cast(params, Map.keys(@types)) | |
|> Changeset.validate_required(:age) | |
|> Changeset.validate_required(:age) | |
|> Changeset.validate_required(:age) | |
assert changeset.valid? == false | |
assert changeset.errors == [age: {"can't be blank", [validation: :required]}] | |
end | |
test "duplicate validate_inclusion checks do add duplicate errors" do | |
params = %{"name" => "tyler", "age" => -1} | |
changeset = | |
{%Person{}, @types} | |
|> Changeset.cast(params, Map.keys(@types)) | |
|> Changeset.validate_required(:age) | |
|> Changeset.validate_inclusion(:age, 0..10) | |
|> Changeset.validate_inclusion(:age, 0..10) | |
assert changeset.valid? == false | |
assert changeset.errors == [ | |
{:age, {"is invalid", [{:validation, :inclusion}, {:enum, 0..10}]}}, | |
{:age, {"is invalid", [{:validation, :inclusion}, {:enum, 0..10}]}} | |
] | |
end | |
end | |
describe "on a non-empty struct" do | |
test "when a field is missing from the params, but is present on the struct, it is okay" do | |
params = %{"name" => "tyler"} | |
changeset = | |
{%Person{name: "bob", age: 17}, @types} | |
|> Changeset.cast(params, Map.keys(@types)) | |
|> Changeset.validate_required([:name, :age]) | |
|> Changeset.validate_inclusion(:age, 0..100) # This will not be run for this case | |
|> Changeset.validate_inclusion(:age, 13..19) # This will not be run for this case | |
assert changeset.valid? == true | |
assert changeset.errors == [] | |
end | |
test "when an invalid field is present on the struct and the params are empty, the invalid field stays" do | |
params = %{"name" => "tyler"} | |
changeset = | |
{%Person{name: "bob", age: -1}, @types} | |
|> Changeset.cast(params, Map.keys(@types)) | |
|> Changeset.validate_required([:name, :age]) | |
|> Changeset.validate_inclusion(:age, 0..100) | |
assert changeset.valid? == true | |
assert changeset.errors == [] | |
assert %{age: -1} = Changeset.apply_changes(changeset) | |
end | |
test "when two fields are required and one was present and the other came from the params, that is okay" do | |
params = %{"name" => "tyler"} | |
changeset = | |
{%Person{age: -1}, @types} | |
|> Changeset.cast(params, Map.keys(@types)) | |
|> Changeset.validate_required([:name, :age]) | |
|> Changeset.validate_inclusion(:age, 0..100) | |
assert changeset.valid? == true | |
assert changeset.errors == [] | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment