【问题标题】:How do I test an instance variable in a controller?如何在控制器中测试实例变量?
【发布时间】:2019-10-15 22:41:54
【问题描述】:

我的几个导师告诉我使用控制器规范测试一段代码,即使内容正在通过一些请求规范进行测试。我想知道是否可以在 rspec 控制器测试中测试是否可以将 @user 之类的实例变量设置为 someObjectnil

到目前为止,我已经尝试了几种不同的方法来测试它,但我在 rspec 测试方面的技能至少可以说是初级的。我尝试的第一件事是模拟 current_user 和 'User.get_from_auth_user(current_user)。模拟对象打印良好并输出。问题是......我希望能够说像expect(@user).to equal(mockedUser) 这样的话,但这似乎永远不会奏效。我总是得到类似的东西:

Expect does not equal received
expected is nil
received is { someMockedObject }

这是我要测试的代码。

class ApplicationController < ActionController::Base
  before_action :set_user

  def set_user
    return if current_user.nil?

    @user = User.get_from_auth_user(current_user)
  end
end

我想知道我是否走在正确的道路上,或者有什么更好的选择来全面测试这段代码。我觉得答案应该很简单。

【问题讨论】:

  • 你是在使用 factory 和 databaserewinder 在 db 中创建对象吗
  • @chans 工厂
  • 我选择的替代方法是测试“set_user”返回的内容。我确实期望(controller.set_user).to eq(mockedUser)

标签: ruby-on-rails ruby unit-testing testing rspec


【解决方案1】:

你的导师很愚蠢。虽然了解控制器规范可能会很好,因为它们在旧应用程序中无处不在,但将它们添加到新应用程序并不是一个好的做法。

对于新的 Rails 应用:我们不建议添加 rails-controller-testing gem 到您的应用程序。官方 Rails 团队和 RSpec 核心团队的建议是写 而是请求规格。请求规范允许您专注于单个 控制器动作,但不像控制器测试涉及路由器, 中间件堆栈,以及机架请求和响应。这增加了 现实主义到你正在编写的测试,并有助于避免许多 控制器规范中常见的问题。

DHH 很好地解释了测试控制器的实例变量有什么问题以及你应该做什么:

测试控制器设置了哪些实例变量是不好的 主意。这严重超出了测试的界限 应该知道。你可以测试设置了什么cookies,什么HTTP代码 返回,视图的外观,或者数据库发生了什么变化, 但是测试控制器的内部并不是一个好主意。

这就是为什么 assigns 被提取到单独的 gem 中的原因。这一切都归结为测试你的代码做了什么——而不是它是怎么做的。

见:

【讨论】:

  • 我真的不认为你的导师是愚蠢的。他们可能只是与当前的最佳实践脱节。如果您想要关于如何编写测试的更具体建议,那么我会提出一个新问题并描述您想要测试的应用程序行为。
  • 我的乐观情绪表明这只是他的导师给他的一个学习练习,以便更好地理解 RSpec/Rails 的工作原理,特别是考虑到他已经承认测试并没有真正做任何有用的事情因为它们已经包含在集成规范中。
  • 我的导师绝对不傻。我认为他们主要是想帮助我了解如何测试和模拟事物。我同意不应该在控制器规范即将消失时编写它们。
  • 虽然有更好的教学方法。关于模拟的一个关键点是,了解何时模拟以及模拟的缺点至关重要。首先,控制器测试的问题在于它们模拟了太多围绕控制器的系统,这就是为什么您仍然会遇到路由错误和其他漏掉的错误。
  • “测试控制器的内部不是一个好主意”——为什么不呢?我在这里没有看到任何经过证实的论点,只是强烈表达的观点。
【解决方案2】:

使用assigns(:user) 获取实例变量@user。在您的规范中,@user 是该测试上下文中的一个变量,它与控制器中的 @user 不同。

https://relishapp.com/rspec/rspec-rails/docs/controller-specs

【讨论】:

    【解决方案3】:

    控制器测试用例是这样的

    describe 'GET #set_user' do
      let!(:mock_user) { 
          @mockuserObj = { id: 1, name: "username" }
      }
      before(:each) do
        controller.instance_variable_set(:current_user, @mockuserObj)
        get :set_user
      end
      context 'when current_user is present' do
        it "sets the current user" do
          expect(assigns(:user)).to eq(@mockuserObj)
        end
      end
    end
    

    【讨论】:

      【解决方案4】:

      除了@chans@arieljuod 答案之外,值得一提的是,为了在您的规范中使用assigns,您需要设置rails-controller-testing gem,因为从Rails 5 开始不推荐使用assigns .

      # File actionpack/lib/action_dispatch/testing/test_process.rb, line 6
      def assigns(key = nil)
        raise NoMethodError,
          "assigns has been extracted to a gem. To continue using it,
          add `gem 'rails-controller-testing'` to your Gemfile."
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-06-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多