【问题标题】:Testing non public routes results in KeyError测试非公共路由会导致 KeyError
【发布时间】:2020-09-10 08:38:19
【问题描述】:

我是 Crystal 和 Amber 的新手,我在测试非公共路线时遇到了问题。我使用了 Amber 身份验证生成器,然后为 Job 实体生成了一个脚手架,并将相关路由添加到 routes :auth 块。 当我打开浏览器并尝试直接进入工作路线时,一切都按预期工作,我被重定向到登录页面。

但是当我为 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’

routes.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 文档和 Google 上都没有找到任何信息。我的问题如下:

  • 我应该如何提供身份验证数据?为什么注销时应用程序会重定向?
  • 对于控制器规格是否有任何测试助手可以让用户登录以便可以 使用经过身份验证的用户进行测试?

【问题讨论】:

    标签: authentication integration-testing keyerror crystal-lang amber-framework


    【解决方案1】:

    您需要在初始化函数中包含身份验证处理程序吗?

    @handler.build :auth do
        plug Authenticate.new
    end
    

    [更新]添加了添加登录测试的指针。

    要测试经过身份验证的路由,您可以使用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")
    

    我没有测试代码,根据可用的文档更新了这里的评论。

    【讨论】:

    • 非常感谢。这导致了我的预期。重定向而不是错误。您能否帮助我解决第二个问题,即如何在测试中对用户进行身份验证?
    • 更新了答案
    • 谢谢。这会起作用,但由于每次测试都需要登录请求,因此速度相当慢。我正在寻找一个绕过登录流程并可用于控制器规范的测试助手。
    【解决方案2】:

    对于您的第二个问题,如果您使用的是 Amber 附带的基本身份验证,我将以下内容添加到我的 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
    

    如果您仍在寻找解决方案,希望对您有所帮助!

    【讨论】:

      猜你喜欢
      • 2016-08-31
      • 1970-01-01
      • 1970-01-01
      • 2010-10-01
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      • 2014-09-29
      • 1970-01-01
      相关资源
      最近更新 更多