Marcus

Marcus

Prove - Write simple tests shorter

Prove is a little and experimental lib that provides the macros prove and batch to use in ExUnit.Case.

A prove is just helpful for elementary tests. Prove generates one test with one assert for every prove.

Example:

defmodule NumTest do
  use ExUnit.Case

  import Prove

  defmodule Num do
    def check(0), do: :zero

    def check(x) when is_integer(x) do
      case rem(x, 2) do
        0 -> :even
        1 -> :odd
      end
    end

    def check(_), do: :error
  end

  describe "check/1" do
    prove Num.check(0) == :zero

    batch "returns :odd or :even" do
      prove Num.check(1) == :odd
      prove Num.check(2) == :even
      prove "for big num", Num.check(2_000) == :even
    end

    batch "returns :error" do
      prove Num.check("1") == :error
      prove Num.check(nil) == :error
    end
  end
end

The example above generates the following tests:

$> mix test test/num_test.exs --trace --seed 0

NumTest [test/num_test.exs]
  * prove check/1 (1) (0.00ms) [L#20]
  * prove check/1 returns :odd or :even (1) (0.00ms) [L#23]
  * prove check/1 returns :odd or :even (2) (0.00ms) [L#24]
  * prove check/1 returns :odd or :even for big num (1) (0.00ms) [L#25]
  * prove check/1 returns :error (1) (0.00ms) [L#29]
  * prove check/1 returns :error (2) (0.00ms) [L#30]


Finished in 0.08 seconds (0.00s async, 0.08s sync)
6 proves, 0 failures

Randomized with seed 0

The benefit of prove is that tests with multiple asserts can be avoided.
The example above with regular tests:

...
  describe "check/1" do
    test "returns :zero" do
      assert Num.check(0) == :zero
    end

    test "returns :odd or :even" do
      assert Num.check(1) == :odd
      assert Num.check(2) == :even
      assert Num.check(2_000) == :even
    end

    test "returns :error" do
      assert Num.check("1") == :error
      assert Num.check(nil) == :error
    end
  end
...
$> mix test test/num_test.exs --trace --seed 0

NumTest [test/num_test.exs]
  * test check/1 returns :zero (0.00ms) [L#36]
  * test check/1 returns :odd or :even (0.00ms) [L#40]
  * test check/1 returns :error (0.00ms) [L#46]


Finished in 0.03 seconds (0.00s async, 0.03s sync)
3 tests, 0 failures

Randomized with seed 0

You can find another example in datix.

The disadvantage of these macros is that the tests are containing fewer descriptions. For this reason and also if a prove looks too complicated, a regular test is to prefer.

Most Liked

thojanssens1

thojanssens1

When using batch/prove there’s no more value using prove rather than test/assert?

    batch "returns :error" do
      prove Num.check("1") == :error
      prove Num.check(nil) == :error
    end
    test "returns :error" do
      assert Num.check("1") == :error
      assert Num.check(nil) == :error
    end

You don’t save any code here while saving code is the purpose of prove, if I understood correctly. It’s a little weird, at least to have introduced batch. Or maybe I missed something.

Marcus

Marcus

@thojanssens1 it works as @IloSophiep describes. But you’re right @thojanssens1, the documentation doesn’t explain it well and neither does my post. I will update the docs. @thojanssens1, @IloSophiep thanks for your posts.

IloSophiep

IloSophiep

I think the advantage of prove in these situations is, that it converts each prove to its own test case.

In your example with test you end up with one test case and it has two asserts in it. You need to check on failure which assert failed and the ones after the failed one are not executed / evaluated while the first one fails.

The prove example though creates two test cases, each with one assert. Both test cases are independent and on failure you instantly know which assert / prove is the problem.

And then batch just allows one to group multiple proves so they all start with the same “prefix”, so you get a bit more context.

At least that’s how I understood everything.

Marcus

Marcus

Yes this would break Ecto’s sandbox and even when not the proves/tests would be executed in random order. So, prove makes just sense for little and pure functions. If you have to think about how to write a test with prove then test is the better choice.

thojanssens1

thojanssens1

That would make sense but that is not what @Marcus wrote above. He wrote the equivalent of batch with three proves as one test with three asserts.

Where Next?

Popular in Libraries Top

Qqwy
TypeCheck: Fast and flexible runtime type-checking for your Elixir projects. Core ideas Type- and function specifications are const...
336 13801 100
New
RobertDober
Earmark is a pure-Elixir Markdown converter. It is intended to be used as a library (just call Earmark.as_html), but can also be used as...
239 11851 134
New
arkgil
Hi all! I’m happy to announce that Telemetry v0.3.0 is out! This release marks the conversion from Elixir to Erlang so that all the libr...
New
jakub-zawislak
Hi everyone, I’m coming from the Symfony (PHP) framework. I like Phoenix, but it has a one thing that was build much better in the Symfo...
New
michalmuskala
Another small library today. PersistentEts Hex: persistent_ets | Hex GitHub: GitHub - michalmuskala/persistent_ets Ets table backed by...
New
MRdotB
I needed to reuse React components from my Chrome extension in my Phoenix/LiveView backend. I noticed that for Svelte/Vue, there are live...
New
ahamez
Hi everyone, I’ve been working on this protobuf library for 3 years. We use it in the company I work for, EasyMile, to communicate with ...
New
Antrater
Hi everyone! I’m thrilled to announce a huge thing. We have been developing Elixir Moon Design System for quite a while. We are finally ...
New
bryanjos
Hi, I wanted share a small library we at Revelry Labs made for rendering react components from the server side. There are instructions fo...
New
mtrudel
Bandit is an HTTP server for Plug and WebSock apps. Bandit is written entirely in Elixir and is built atop Thousand Island. It can serve...
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

Sub Categories:

We're in Beta

About us Mission Statement