使用 Elixir 中的 Ecto 测试 repo 类中的覆盖率

Alv*_*aro 0 postgresql unit-testing elixir ecto

我在 Elixir 中有一个非常简单的应用程序,它使用 Ecto 在 Postgres 上的表中运行 SELECT 查询。\n表名称是“person”,有 2 列:“name”和“age”。

\n

这是应用程序:

\n
/ecto_app\n |-application.ex\n |-repo.ex\n
Run Code Online (Sandbox Code Playgroud)\n

应用程序.ex:

\n
defmodule EctoApp.Application do\n\n  use Application\n\n  require Logger\n\n  @impl true\n  def start(_type, _args) do\n    children = [\n      EctoApp.Repo\n    ]\n\n    opts = [strategy: :one_for_one, name: EctoApp.Supervisor]\n    Logger.debug("Starting Ecto App")\n    Supervisor.start_link(children, opts)\n  end\nend\n
Run Code Online (Sandbox Code Playgroud)\n

回购协议:

\n
defmodule EctoApp.Repo do\n  use Ecto.Repo,\n    otp_app: :ecto_app,\n    adapter: Ecto.Adapters.Postgres\n\n  import Ecto.Query\n\n  require Logger\n\n\n  def get_people() do\n    query = from p in "person",\n            select: [p.name, p.age]\n    result= EctoApp.Repo.all(query)\n    Logger.debug(result)\n  end\n\nend\n
Run Code Online (Sandbox Code Playgroud)\n

我正在使用这两个库:

\n
[\n  {:ecto_sql, "~> 3.0"},\n  {:postgrex, ">= 0.0.0"}\n]\n
Run Code Online (Sandbox Code Playgroud)\n

该应用程序工作正常,但问题是我想为存储库进行单元测试,但我可以 xc2xb4t 弄清楚如何模拟 Postgres 连接,因为我不想启动 Postgres 实例来运行测试。

\n

有谁知道如何 Mock 或我可以使用哪个库来“模拟”与 Postgres 的连接,以便我可以对 repo 类进行测试覆盖?

\n

谢谢!

\n

Eve*_*ett 5

您不需要像模拟其他功能一样模拟 PostGres 连接。这是因为 PostGres 可以将操作包装在可撤消事务中。结论是,您的测试使用真实的连接并对真实的数据库发出真实的请求,但该数据库是专用于测试并通过特殊适配器发出查询的数据库。这是迄今为止实现数据库支持的应用程序测试覆盖率的最常见方法;尝试模拟数据库连接会导致脆弱的测试充满误报,因此这种做法并不常见,尤其是当针对真实数据库发出所有请求非常简单时。

以下是在代码中促进这一点的重要部分:

  1. 在应用程序配置中指定沙箱适配器和专用测试数据库,例如通过配置文件:
# config/dev.exs
config :my_app, MyApp.Repo,
  pool: DBConnection.ConnectionPool,
  database: "something_dev",
  # ... etc...

# config/test.exs
config :my_app, MyApp.Repo,
  pool: Ecto.Adapters.SQL.Sandbox,
  database: "something_test",
  # ... etc...
Run Code Online (Sandbox Code Playgroud)
  1. 您的测试必须在运行查询之前检查Repo适配器的使用情况。这就是将操作包装在事务中的原因,以便您可以安全地重新运行测试,而无需进行过多的清理。一个好的地方是在setup 回调中,它在模块中的每个测试之前运行:
defmodule MyApp.SomeTest do
  use ExUnit.Case # you may require async: false

  alias Ecto.Adapters.SQL.Sandbox
  alias MyApp.Repo

  setup do
    Sandbox.checkout(Repo)
  end

  describe "get_people/0" do
    test "select" do
      # insert fake records here
      assert [] = Repo.get_people()
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

还有一些提示:

  1. 正如所写的,您的get_people/0函数不会返回result. 请记住,Elixir 依赖于隐式返回,因此它返回最后一个操作的结果。在您的情况下,返回的结果将是调试语句的结果,这只是一个:ok而不是result您所期望的。

  2. 不要忘记您可能需要对测试数据库运行迁移——这不是自动发生的事情。您可以在测试之前创建一个别名mix.exs来执行,或者您可以只记得针对测试数据库运行等。mix ecto.migrateMIX_ENV=test mix ecto.drop

  3. 您经常会发现自动构建数据“固定装置”(即正确形状的虚假记录)很有帮助。ex_machina包是执行此操作的一种便捷方法,但无论您使用包还是推出自己的装置,您的测试都需要使用您期望的任何测试记录来准备数据库,作为标准中“安排”步骤的一部分“安排-行动-断言”测试方法。

如果您的情况确实需要模拟数据库连接,那么我会推荐一种依赖于一种“依赖注入”的简单模式,例如您可以将模块重写Repo为函数参数或从配置中提取的内容,例如

def get_people(repo \\ Repo)
  query = from p in "person",
    select: [p.name, p.age]
  repo.all(query)
end
Run Code Online (Sandbox Code Playgroud)