blatyo

blatyo

Conduit Core Team

I’m testing some complex ecto sql builder stuff. So I pass data to generate an ecto query and I want to validate that it produces the right query. So, I’m using the Repo.to_sql function to generate SQL and then asserting that matches the expected SQL.

That works OK, but when the SQL is long, it makes it hard to understand which parts of the query differ. I wrote something that can format SQL, because I thought it’d improve the diff output that exunit provides. However, that just added some more whitespace to a single line diff between the two SQL queries. Here’s an example:

What I would like is to have a test failure that would wrap on the newlines instead. Ideally, it would look something like this with the diff coloring elixir does:

  1) test formats SQL (SQLFormatterTest)
     test/sql_formatter_test.exs:5
     Assertion with == failed
     code:  assert SQLFormatter.format("SELECT * FROM users") == "SELECT\n  *\nFROM\n  user\nWHERE\n  id = 1\n"
     left:  """
            SELECT
              *
            FROM
              users
            """
     right: """
            SELECT
              *
            FROM
              user
            WHERE
              id = 1
            """
     stacktrace:
       test/sql_formatter_test.exs:6: (test)

I’m curious if anyone has any suggestions for how to get better test output?

Showing Posts 1 to 4

codeanpeace

codeanpeace

It sounds like something you could do with the ExUnit.Formatter module.

format_test_failure(test, failures, counter, width, formatter)
Receives a test and formats its failures.

Examples

iex> failure = {:error, catch_error(raise "oops"), _stacktrace = []}
iex> formatter_cb = fn _key, value -> value end
iex> test = %ExUnit.Test{name: :"it works", module: MyTest, tags: %{file: "file.ex", line: 7}}
iex> format_test_failure(test, [failure], 1, 80, formatter_cb)
"  1) it works (MyTest)\n     file.ex:7\n     ** (RuntimeError) oops\n"

source: docs for format_test_failure/5

sodapopcan

sodapopcan

The problem I run into here is that format_test_failure seems to escape \n as \\n. AFAIK (and I would love to be proved wrong) I think you have to do the formatting yourself.

This is a cobbled together version from a formatter I have. I was hooking into the standard formatter and using format_test_failure so I had to remove that which of course removes all the meta data like test name and line number (though it’s easily added as all of that info is available between error and meta). All this has is left and right but it does work if you want to use it as a starting point!

Again, hopefully I’m wrong and there is an easier way.

defmodule Formatter do
  use GenServer

  def init(_opts) do
    {:ok, %{failures: []}}
  end

  def handle_cast({:test_finished, %{state: {:failed, errors}} = _test} = _event, state) do
    error =
      errors
      |> Enum.map(fn {:error, error, _meta} ->
        left =
          String.split(error.left, "\n")
          |> Enum.map(& "      " <> &1)
          |> Enum.join("\n")

        right =
          String.split(error.right, "\n")
          |> Enum.map(&"       " <> &1)
          |> Enum.join("\n")

        "left: \"\"\"\n" <> left <> "\"\"\"\n\nright: \"\"\"\n" <> right <> "\"\"\""
      end)
      |> Enum.join("\n")

    state = %{state | failures: [error | state.failures]}

    {:noreply, state}
  end

 def handle_cast({:suite_finished, _} = _event, state) do
   if Enum.any?(state.failures) do
     failures =
       state.failures
       |> Enum.reverse()
       |> Enum.join("\n")

     IO.puts("""

       Failures:

     #{failures}
     """)
   end

   {:noreply, state}
 end

  def handle_cast(_event, state) do
    {:noreply, state}
  end
end

ExUnit.start(formatters: [Formatter])

defmodule TestIt do
  use ExUnit.Case

  test "thing" do
    assert """
    line 1
    line 2
    line 3
    """ == """
    line oops
    line 2
    line 3
    """
  end
end
egze

egze

I’ve been doing similar things at work for showing meaningful diffs between 2 JSON files. I had to normalize both: sort keys (json keys don’t have order), pretty print, and only then diff.

See these links for inspiration:

  1. Semantic Diff for SQL
  2. pg_query fingerprint
  3. difftastic for SQL
blatyo

blatyo OP

Conduit Core Team

Thanks for the suggestions. I was definitely in the headspace of thinking I’d have to write a custom assertion. I forgot ex_unit even had formatters, so that will be a useful area to explore.

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
ChrisAmelia
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication): toke...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews