Как реализовать assert_difference для ExUnit

Я хотел бы проверить, как функция что-то изменяет в базе данных. Я борюсь с эквивалентом ExUnit следующего тестового примера ActiveSupport::TestCase:

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

Версия RSpec более элегантна, и из-за использования в ней лямбда-выражений я подумал, что ее можно перенести на 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

Есть ли более элегантный (читай: функциональный) способ сделать это, чем этот:

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

person Carsten    schedule 28.09.2016    source источник
comment
Более функциональный способ обнаружения change (читай «обнаружение мутации объекта») звучит немного странно для меня :) Там нет состояния, все объекты неизменяемы, поэтому наиболее функциональным будет тот способ, который вы предложили.   -  person Aleksei Matiushkin    schedule 28.09.2016


Ответы (1)


Вы можете использовать макросы, чтобы получить что-то близкое к примерам TestUnit и RSpec из 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

Но я бы не стал использовать его, если только он не будет использоваться много, иначе это только затруднит выполнение кода для всех (возможно), кроме автора. Однако, если вы используете его, вы можете переместить его в другой модуль и импортировать его в свои тестовые модули.

person Dogbert    schedule 28.09.2016
comment
Спасибо! Для меня это тоже было хорошим упражнением в изучении агентов :) - person Carsten; 12.11.2016