繁体   English   中英

在 Elixir 中使用 Ecto 在 repo class 中测试覆盖率

[英]Test coverage in repo class using Ecto in Elixir

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

这是应用程序:

/ecto_app
 |-application.ex
 |-repo.ex

应用程序.ex:

defmodule EctoApp.Application do

  use Application

  require Logger

  @impl true
  def start(_type, _args) do
    children = [
      EctoApp.Repo
    ]

    opts = [strategy: :one_for_one, name: EctoApp.Supervisor]
    Logger.debug("Starting Ecto App")
    Supervisor.start_link(children, opts)
  end
end

回购.ex:

defmodule EctoApp.Repo do
  use Ecto.Repo,
    otp_app: :ecto_app,
    adapter: Ecto.Adapters.Postgres

  import Ecto.Query

  require Logger


  def get_people() do
    query = from p in "person",
            select: [p.name, p.age]
    result= EctoApp.Repo.all(query)
    Logger.debug(result)
  end

end

我正在使用这两个库:

[
  {:ecto_sql, "~> 3.0"},
  {:postgrex, ">= 0.0.0"}
]

该应用程序运行良好,但问题是我想为回购进行单元测试,但我不知道如何模拟 Postgres 连接,因为我不想启动 Postgres 实例来运行测试。

有没有人知道如何模拟或我可以使用哪个库来“模拟”与 Postgres 的连接,这样我就可以在回购 class 上进行测试?

谢谢!

您不需要像模拟其他功能那样模拟 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...
  1. 在运行查询之前,您的测试必须使用适配器检查Repo 这就是将操作包装在事务中的原因,因此您可以安全地重新运行测试而无需过多清理。 一个好的地方是在setup callback内部,它在模块中的每个测试之前运行:
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

还有几点建议:

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

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

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

如果你的情况确实需要你模拟数据库连接,那么我会推荐一个依赖于一种“依赖注入”的简单模式,例如,你可以将Repo模块覆盖为 function 参数或从配置中提取的东西,例如

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

我建议在容器中运行 PostgreSQL,然后使用 Ecto Sandbox。 https://hexdocs.pm/ecto_sql/Ecto.Adapters.SQL.Sandbox.html

带有自己的测试套件,并且有专门的文档介绍如何使用 Ecto 进行测试

Ecto.Adapters.SQL.Sandbox将用于测试,但无法避免启动实际的数据库实例,主要是因为使用模拟连接进行测试意义不大。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM