如何在不设置实际数据库的情况下测试Phoenix控制器?

问题描述 投票:0回答:2

我正在凤凰城测试一些控制器,其中一些设置太复杂,无法包含在测试中。例如,我需要测试索引函数:

defmodule ApiWeb.StudentController do
use ApiWeb, :controller

alias Api.Students

action_fallback MsnrApiWeb.FallbackController

def index(conn, %{"semester_id" => semester_id}) do
  students = Students.list_students(semester_id)
  render(conn, "index.json", students: students)
end

这是路由器:

resources "/semesters", SemesterController, except: [:new, :edit] do
  ... 
  resources "/students", StudentController, except: [:new, :edit]

list_students 函数不是常规函数:

def list_students(semester_id) do
  from(ss in StudentSemester,
    join: s in Student,
    on: ss.semester_id == ^semester_id and ss.student_id == s.user_id,
    join: u in assoc(s, :user),
    preload: [student: {s, user: u}],
    select: ss
  )
  |> Repo.all()
end

我已经测试了 list_students 函数,并在所有表中进行了所有需要的设置(实际上插入到数据库中),但现在我只想测试控制器和视图。我怎样才能做到这一点而不重复我已经为 list_students 所做的所有设置?

有没有办法在不改变原始代码的情况下模拟该函数?

testing controller mocking elixir phoenix-framework
2个回答
0
投票

您的测试设置有多复杂?这是一个广泛的问题,但您可以使用一些工具/策略。

理想情况下,您的设置应该易于复制。您可以使用类似 ex_machina 的东西来设置工厂,以便轻松插入数据。这样,将数据导入数据库就变得非常简单:

# to insert a single student
insert(:student, semester_id: "someId")

# to insert a list of students
insert_list(10, :student, semester_id: "someId")

但是,如果您根本不想将数据插入数据库并且您确实想完全避免调用该函数,则可以使用 meck 并将类似的内容添加到您的测试文件中:

:meck.new(Api.Students)
:meck.expect(Api.Students, :list_students, fn semester_id -> [some_results] end)

您还可以使用 Mox 完成类似的操作,只需几个步骤。但这些库更常用于模拟对第三方服务的请求,将数据插入数据库确实不应该有问题。如果您的设置太复杂,您可以定义一些辅助函数并重用它们。

但无论哪种方式,在那之后,您应该能够像往常一样测试您的控制器,所以类似于:

test "list_students", %{conn: conn} do
  result = get(conn, Routes.semesters_students_path(conn, :index), %{semester_id: some_semester_id})
  assert [] == json_response(conn, 200)
end

0
投票

如果你想模拟控制器函数的响应,你应该使用 Mock (hexdoc),你可以硬编码所需的响应,文档应该帮助你开始,它应该易于使用,并且它在 Elixir 中。然而,对于有效的测试来说,这并不明智。

© www.soinside.com 2019 - 2024. All rights reserved.