在 Elixir 中使用 Ecto 在 repo class 中测试覆盖率
Test coverage in repo class using Ecto in Elixir
我在 Elixir 中有一个使用 Ecto 的非常简单的应用程序,用于 运行 在 Postgres 上的 table 中的 SELECT 查询。
table 姓名是“人”,有两列:“姓名”和“年龄”。
这是应用程序:
/ecto_app
|-application.ex
|-repo.ex
application.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
repo.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
我正在使用这 2 个库:
[
{:ecto_sql, "~> 3.0"},
{:postgrex, ">= 0.0.0"}
]
该应用程序运行良好,但问题是我想为回购进行单元测试,但我不知道如何模拟 Postgres 连接,因为我不想启动 Postgres 实例运行 测试。
有没有人知道如何 Mock 或我可以使用哪个库来“模拟”与 Postgres 的连接,这样我就可以在 repo 上进行测试 class?
谢谢!
我建议 运行 在容器中使用 PostgreSQL,然后使用 Ecto Sandbox。 https://hexdocs.pm/ecto_sql/Ecto.Adapters.SQL.Sandbox.html
您不需要像模拟其他功能那样模拟 PostGres 连接。这是因为 PostGres 可以将操作包装在一个可撤销的事务中。要点是您的测试使用 real 连接并对 real 数据库发出 real 请求,但是该数据库专用于通过特殊适配器进行测试和发出查询。这是迄今为止实现 database-backed 应用程序测试覆盖率的最常见方法;尝试模拟数据库连接会导致脆弱的测试充满误报,因此这种做法并不常见,尤其是当针对真实数据库发出所有请求如此简单时。
以下是在您的代码中促进这一点的重要部分:
- 在您的应用配置中指定沙盒适配器和专用测试数据库,例如通过配置文件:
# 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...
- 您的测试必须在 运行ning 查询之前使用适配器检查
Repo
。这就是将操作包装在事务中的内容,因此您可以安全地 re-run 测试而无需过多清理。一个很好的地方是在 setup callback 内部,在模块中的每个测试之前 运行s:
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
还有一些建议:
您编写的 get_people/0
函数不会 return result
。请记住,Elixir 依赖于隐式 returns,因此它 returns 是最后一个操作的结果。在你的情况下,returned 将是 debug 语句的结果,它只是一个 :ok
而不是你的 result
期待。
不要忘记您可能需要 运行 迁移到您的测试数据库——这不是自动发生的事情。您可以在 mix.exs
中创建一个别名以在测试之前执行 mix ecto.migrate
,或者您可以只记住 运行 MIX_ENV=test mix ecto.drop
等针对测试数据库。
您经常会发现自动构建数据“夹具”(即具有正确形状的假记录)很有帮助。 ex_machina 包是执行此操作的便捷方法,但无论您使用包还是滚动自己的固定装置,您的测试都需要使用您期望的任何测试记录来准备数据库,作为“安排”的一部分标准“arrange-act-assert”测试方法的步骤。
如果你的情况确实需要你模拟数据库连接,那么我会推荐一个依赖于一种“依赖注入”的简单模式,例如您可以在其中将 Repo
模块作为函数参数或从配置中提取的内容覆盖,例如
def get_people(repo \ Repo)
query = from p in "person",
select: [p.name, p.age]
repo.all(query)
end
ecto comes with its own testing suite and it has dedicated documentation on How to Test with Ecto.
Ecto.Adapters.SQL.Sandbox
用于测试 但是 没有办法避免启动实际的数据库实例,主要是因为使用 mocked 没有什么意义用于测试的连接。
我在 Elixir 中有一个使用 Ecto 的非常简单的应用程序,用于 运行 在 Postgres 上的 table 中的 SELECT 查询。 table 姓名是“人”,有两列:“姓名”和“年龄”。
这是应用程序:
/ecto_app
|-application.ex
|-repo.ex
application.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
repo.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
我正在使用这 2 个库:
[
{:ecto_sql, "~> 3.0"},
{:postgrex, ">= 0.0.0"}
]
该应用程序运行良好,但问题是我想为回购进行单元测试,但我不知道如何模拟 Postgres 连接,因为我不想启动 Postgres 实例运行 测试。
有没有人知道如何 Mock 或我可以使用哪个库来“模拟”与 Postgres 的连接,这样我就可以在 repo 上进行测试 class?
谢谢!
我建议 运行 在容器中使用 PostgreSQL,然后使用 Ecto Sandbox。 https://hexdocs.pm/ecto_sql/Ecto.Adapters.SQL.Sandbox.html
您不需要像模拟其他功能那样模拟 PostGres 连接。这是因为 PostGres 可以将操作包装在一个可撤销的事务中。要点是您的测试使用 real 连接并对 real 数据库发出 real 请求,但是该数据库专用于通过特殊适配器进行测试和发出查询。这是迄今为止实现 database-backed 应用程序测试覆盖率的最常见方法;尝试模拟数据库连接会导致脆弱的测试充满误报,因此这种做法并不常见,尤其是当针对真实数据库发出所有请求如此简单时。
以下是在您的代码中促进这一点的重要部分:
- 在您的应用配置中指定沙盒适配器和专用测试数据库,例如通过配置文件:
# 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...
- 您的测试必须在 运行ning 查询之前使用适配器检查
Repo
。这就是将操作包装在事务中的内容,因此您可以安全地 re-run 测试而无需过多清理。一个很好的地方是在 setup callback 内部,在模块中的每个测试之前 运行s:
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
还有一些建议:
您编写的
get_people/0
函数不会 returnresult
。请记住,Elixir 依赖于隐式 returns,因此它 returns 是最后一个操作的结果。在你的情况下,returned 将是 debug 语句的结果,它只是一个:ok
而不是你的result
期待。不要忘记您可能需要 运行 迁移到您的测试数据库——这不是自动发生的事情。您可以在
mix.exs
中创建一个别名以在测试之前执行mix ecto.migrate
,或者您可以只记住 运行MIX_ENV=test mix ecto.drop
等针对测试数据库。您经常会发现自动构建数据“夹具”(即具有正确形状的假记录)很有帮助。 ex_machina 包是执行此操作的便捷方法,但无论您使用包还是滚动自己的固定装置,您的测试都需要使用您期望的任何测试记录来准备数据库,作为“安排”的一部分标准“arrange-act-assert”测试方法的步骤。
如果你的情况确实需要你模拟数据库连接,那么我会推荐一个依赖于一种“依赖注入”的简单模式,例如您可以在其中将 Repo
模块作为函数参数或从配置中提取的内容覆盖,例如
def get_people(repo \ Repo)
query = from p in "person",
select: [p.name, p.age]
repo.all(query)
end
ecto comes with its own testing suite and it has dedicated documentation on How to Test with Ecto.
Ecto.Adapters.SQL.Sandbox
用于测试 但是 没有办法避免启动实际的数据库实例,主要是因为使用 mocked 没有什么意义用于测试的连接。