我是Crystal和Amber的新手,在测试非公共路由时遇到了问题。我使用了Amber身份验证生成器,然后为Job实体生成了一个脚手架,并向路由添加了相关的路由:auth-block。当我打开浏览器,尝试直接转到工作路线时,一切都如预期一样,我会被重定向到登录页面。
但是当我为JobsController执行生成的测试时,我得到了以下错误:
1) JobControllerTest renders job index template
Missing hash key: :auth (KeyError)
from ../../.asdf/installs/crystal/0.35.1/src/hash.cr:1030:9 in ‘[]’
from lib/amber/src/amber/pipes/pipeline.cr:19:15 in ‘call’
from lib/garnet_spec/src/garnet_spec/controller/test.cr:25:7 in ‘process_request’
from spec/controllers/job_controller_spec.cr:20:1 in ‘get’
from spec/controllers/job_controller_spec.cr:39:5 in ‘->’
from ../../.asdf/installs/crystal/0.35.1/src/primitives.cr:255:3 in ‘internal_run’
from ../../.asdf/installs/crystal/0.35.1/src/spec/example.cr:33:16 in ‘run’
from ../../.asdf/installs/crystal/0.35.1/src/spec/context.cr:18:23 in ‘internal_run’
from ../../.asdf/installs/crystal/0.35.1/src/spec/context.cr:330:7 in ‘run’
from ../../.asdf/installs/crystal/0.35.1/src/spec/context.cr:18:23 in ‘internal_run’
from ../../.asdf/installs/crystal/0.35.1/src/spec/context.cr:147:7 in ‘run’
from ../../.asdf/installs/crystal/0.35.1/src/spec/dsl.cr:270:7 in ‘->’
from ../../.asdf/installs/crystal/0.35.1/src/primitives.cr:255:3 in ‘run’
from ../../.asdf/installs/crystal/0.35.1/src/crystal/main.cr:45:14 in ‘main’
from ../../.asdf/installs/crystal/0.35.1/src/crystal/main.cr:114:3 in ‘main’
路线.cr
routes :auth do
...
resources "jobs", JobController
end
JobControllerTest.cr
...
class JobControllerTest < GarnetSpec::Controller::Test
getter handler : Amber::Pipe::Pipeline
def initialize
@handler = Amber::Pipe::Pipeline.new
@handler.build :web do
plug Amber::Pipe::Error.new
plug Amber::Pipe::Session.new
plug Amber::Pipe::Flash.new
end
@handler.prepare_pipelines
end
end
describe JobControllerTest do
subject = JobControllerTest.new
it “renders job index template” do
Job.clear
response = subject.get “/jobs” # -> line 39 where the error happens
response.status_code.should eq(302)
response.body.should contain(“jobs”)
end
end
...
我在Ambers文档中找不到任何信息,在谷歌上也找不到。我的问题如下:
- 我应该如何提供身份验证数据?为什么我有,因为应用程序在注销时重定向
- 对于控制器规格,是否有任何测试助手可以让用户登录,以便使用经过身份验证的用户进行测试
是否需要在initialize函数中包含auth处理程序?
@handler.build :auth do
plug Authenticate.new
end
[Update]添加了添加登录测试的指针。
要测试经过身份验证的路由,可以使用中的系统测试https://docs.amberframework.org/amber/guides/testing/system-tests
我没有看到太多关于所有可用的API调用来进行系统测试的文档。但从这段代码中我了解到,一旦页面加载,您就可以填写登录名和密码,并模拟点击事件。https://github.com/amberframework/garnet-spec/blob/master/src/garnet_spec/system_test.cr
fill(:class_name, "login", "test_username")
fill(:class_name, "password", "test_pass")
click(:class_name, "login-button")
我还没有测试过代码,根据可用的文档更新了这里的注释。
对于您的第二个问题,如果您使用的是Amber附带的基本Auth,我将以下内容添加到我的spec_helper.cr
中,并能够通过从类继承来对经过身份验证的用户进行控制器测试。它确实需要crystagiri
碎片,以便您能够获得csrf_token。
require "../spec_helper"
require "crystagiri"
def login_params(csrf_token)
params = [] of String
params << "email=admin@example.com"
params << "password=password"
params << "_csrf=#{csrf_token}"
params.join("&")
end
def create_user
user = User.new(email: "admin@example.com", first_name:"Test", last_name:"User")
user.password = "password"
user.save
user
end
def get_csrf_token(response_body)
html = Crystagiri::HTML.new response_body
csrf_token = String.new
html.where_tag("input") do |back|
csrf_token = back.node.attributes[2].content
break
end
csrf_token
end
class ApplicationControllerTest < GarnetSpec::Controller::Test
getter handler : Amber::Pipe::Pipeline
getter current_user = User.new
getter csrf_token = String.new
setter current_user
def initialize
@handler = Amber::Pipe::Pipeline.new
@handler.build :web do
plug Citrine::I18n::Handler.new
plug Amber::Pipe::Error.new
plug Amber::Pipe::Logger.new
plug Amber::Pipe::Session.new
plug Amber::Pipe::Flash.new
plug Amber::Pipe::CSRF.new
plug CurrentUser.new
end
@handler.build :auth do
plug Citrine::I18n::Handler.new
plug Amber::Pipe::Error.new
plug Amber::Pipe::Logger.new
plug Amber::Pipe::Session.new
plug Amber::Pipe::Flash.new
plug Amber::Pipe::CSRF.new
plug CurrentUser.new
plug Authenticate.new
end
@handler.prepare_pipelines
@current_user = create_user
end
def login_user
response = get "/signin"
@csrf_token = get_csrf_token(response.body)
post "/session", body: login_params(csrf_token), headers: HTTP::Headers{"Cookie" => response.headers["Set-Cookie"]}
end
end
然后在你的class_controller_spec.cr
中,当你需要用户在请求之前登录时,你会有如下内容
class AccountControllerTest < ApplicationControllerTest
end
describe AccountControllerTest do
subject = AccountControllerTest.new
it "renders account index template" do
subject.current_user = create_user
response_headers = subject.login_user.headers
response = subject.get "/accounts", headers: HTTP::Headers{"Cookie" => response_headers["Set-Cookie"]}
response.status_code.should eq(200)
response.body.should contain("accounts")
end
end
如果你还在寻找解决方案,希望这会有所帮助!