Cleaning database between tests with MongoDB (no Ecto)

What’s the best way to set up a test database for testing an app that uses MongoDB without Ecto?

I’d like the database to be in a clean state between each test.

2 Likes

You can drop the db before every test case with:

setup do
   Mongo.command(:mongo_name, %{dropDatabase: 1})
end

Sadly this way the db will contain the last test case after all tests have finished, but I couldn’t get it working inside on_exit

edit: I just noticed this question isn’t 2 months, but 3 years and 2 months old. Still, I hope someone will find it useful one day

2 Likes

You can use the mtools to create a MongoDB deployment (Standalone, Replica-Set etc) for the test cases. Then it does not matter, if database of the last test case is not dropped.

If you use this driver you can take a look at the CollectionCase template:

defmodule CollectionCase do
  use ExUnit.CaseTemplate

  @seeds ["127.0.0.1:27017", "127.0.0.1:27018", "127.0.0.1:27019"]

  setup_all do
    assert {:ok, pid} = Mongo.start_link(database: "mongodb_test", seeds: @seeds, show_sensitive_data_on_connection_error: true)
    Mongo.admin_command(pid, [configureFailPoint: "failCommand", mode: "off"])
    Mongo.drop_database(pid)
    {:ok, [pid: pid]}
  end

  setup do
    {:ok, catcher} = EventCatcher.start_link()
    on_exit(fn -> EventCatcher.stop(catcher) end)
    [catcher: catcher]
  end

  using do
    quote do
      import CollectionCase
    end
  end

  defmacro unique_collection do
    {function, _arity} = __CALLER__.function
    "#{__CALLER__.module}.#{function}"
    |> String.replace(" ", "_")
    |> String.replace(".", "_")
    |> String.replace(":", "_")
    |> String.downcase()
  end
end

Using the MongoDB 4.4 you can simulate network-errors by using the failCommand, like this

    cmd = [
      configureFailPoint: "failCommand",
      mode: [times: 1],
      data: [errorCode: 6, failCommands: ["count"]]
    ]

    Mongo.admin_command(top, cmd)
   {:error, %Error{code: 6, retryable_reads: true}} = Mongo.count(top, coll, %{})

But if you use this, then you need to turn off the async: false.

The EventCatcher monitors the commands that sends the driver to MongoDB. You can test whether the program sends the right sequence of commands to the MongoDB.

assert [:count | _] = EventCatcher.retryable_read_events(catcher) |> Enum.map(fn event -> event.command_name end)
2 Likes