Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement an "assert_difference" for ExUnit

I'd like to test how a function changes something in the database. I'm struggling with an ExUnit equivalent of the following ActiveSupport::TestCase test case:

test "creates a database record" do
  post = Post.create title: "See the difference"
  assert_difference "Post.published.count" do
    post.publish!
  end
end

The RSpec version is more elegant and, because of its use of lambdas, something I thought was portable to Elixir/ExUnit.

it "create a database record" do
  post = Post.create title: "See the difference"
  expect { post.publish! }.to change { Post.count }.by 1
end

Is there a more elegant (read: functional) way to do it than this:

test "creates a database record", %{conn: conn} do
  records_before = count_records
  post(conn, "/articles")
  records_after  = count_records

  assert records_before == (records_after - 1)
end

defp count_records do
  MyApp.Repo.one((from a in MyApp.Article, select: count("*"))
end
like image 201
Carsten Avatar asked Oct 19 '25 01:10

Carsten


1 Answers

You can use macros to get something close to the TestUnit and RSpec examples from Ruby:

defmacro assert_difference(expr, do: block) do
  quote do
    before = unquote(expr)
    unquote(block)
    after_ = unquote(expr)
    assert before != after_
  end
end

defmacro assert_difference(expr, [with: with], do: block) do
  quote do
    before = unquote(expr)
    unquote(block)
    after_ = unquote(expr)
    assert unquote(with).(before) == after_
  end
end

test "the truth" do
  {:ok, agent} = Agent.start_link(fn -> 0 end)

  assert_difference Agent.get(agent, &(&1)) do
    Agent.update(agent, &(&1 + 1))
  end

  {:ok, agent} = Agent.start_link(fn -> 0 end)

  assert_difference Agent.get(agent, &(&1)), with: &(&1 + 2) do
    Agent.update(agent, &(&1 + 2))
  end
end

But I wouldn't use it unless it's going to be used a lot or else this would only make the code harder to follow for everyone (possibly) except the author. If you do use it though, you might want to move it to a different module and import that in your test modules.

like image 159
Dogbert Avatar answered Oct 21 '25 03:10

Dogbert