【问题标题】:Rspec - Is it possible to stub a hashRspec - 是否可以存根哈希
【发布时间】:2019-03-06 15:18:07
【问题描述】:

我正在做一些单元测试。其中一个使用在我的应用程序MyBigApp::Env 中设置的特定配置变量,如下所示:

{:country=>'uk', :another_hosts=>["192.168.99.105"]}

所以我用MyBigApp::Env.country访问它

但是在我的单元测试中,我希望用于测试的 country 成为一些东西。

使用 rspec 我见过 stub 但无法让它工作 - 任何我出错的想法:

MyBigApp::Env.stub(:[]).with('country').and_return('gr')

也试过这个(如上所示已弃用):

allow(MyBigApp::Env).to receive('country').and_return('gr')

其实作为一个测试,我也试过了:

my_hash = {:uri=>nil}
allow(my_hash).to receive(:[]).with(:uri).and_return('Over written!')
p my_hash

也没有更新 - 它只是返回了{:uri=>nil}

作为一种解决方法,目前我必须将环境变量保存在 before(each) 块中的临时变量中,然后将其返回到 after(each) 中的原始变量。这对我来说真的很冒险。我在想想象服务正在运行并且有人运行单元测试,它可能会在测试运行的那个小实例中影响最终用户。

任何帮助将不胜感激。

谢谢

【问题讨论】:

    标签: ruby rspec


    【解决方案1】:

    是的,可能,但请记住,存根仅在您触发/调用您存根/模拟的方法时才有效

    my_hash = {:uri=>nil}
    allow(my_hash).to receive(:[]).with(:uri).and_return('Over written!')
    p my_hash[:url] # it will be 'Over written!'
    

    【讨论】:

      【解决方案2】:

      这对我有用:

      my_hash = {:uri=>nil}
      allow(my_hash).to receive(:[]).with(:uri).and_return('Over written!')
      expect(my_hash[:uri]).to eq "Over written!"
      

      在您的示例测试用例中,您只是调用了p my_hash,而实际上并未调用[] 方法。

      就为什么这不适用于MyBigApp::Env,嗯,这真的取决于它是什么类。可能的任何方法.country 实际上都不会调用[]

      真的,如果你调用MyBigApp::Env['country'] 和存根MyBigApp::Env 来接收[]'country',它应该工作。

      关于您担心从测试中更改正在运行的应用程序的行为......这些是什么类型的测试?!针对实时生产应用程序运行单元测试会非常奇怪。你如何想象它会改变你的生产应用程序的代码? Env 哈希只是存在于内存中,对吗?

      无论如何,您永远不必担心您的测试会改变“最终用户”的体验。始终在完全隔离的环境中运行测试,这意味着不要使用相同的数据库。实际上,测试数据库通常在每次测试后被擦除。

      【讨论】:

      • 所以当您说if you call MyBigApp::Env['country'] and stub MyBigApp::Env to receive [] with 'country', it should work. 时,您的意思是:allow(MyBigApp::Env).to receive('country').and_return('gr')。我已经试过了,但没有运气?
      • MyBigApp::Env['country']MyBigApp::Env.country 之间存在差异。
      【解决方案3】:

      只是想提出一个非存根替代方案。例如:

      def code_under_test
        key = 'country'
      
        # ... maybe lots of code
      
        value = MyBigApp::Env[key] # deep inside some classes
      
        # ... lots more code
      
        "This is the #{value}"
      end
      

      MyBigApp::Env 被硬编码在代码深处,需要存根暴露了依赖关系,失去了 OOP 封装的好处。

      如果是这样的话会容易得多:

      def code_under_test(config_vars = MyBigApp::Env)
        "This is the #{config_vars['country']}"
      end
      
      it 'should return my country value' do
        value = previous_code_under_test('country' => 'TEST VALUE')
        expect(value).to eq("This is the TEST VALUE")
      end
      

      不需要存根,只需简单的旧方法调用。

      【讨论】:

      • 这是“依赖注入”,关于它好不好有很多争论,但我认为没关系:)
      • 是的。不过不要认为有太多的争论。我很确定大家普遍认为 SOLID 原则是“好事”,这是“D”。
      • 这不回答问题,鼓励test-induced design damage
      • 模拟有它的位置,例如外部/昂贵的服务,这只是一个简单的值散列和替代解决方案。说注意 SOLID 会“造成损害”,这有点夸张。
      猜你喜欢
      • 2011-06-20
      • 2010-12-01
      • 2013-06-23
      • 1970-01-01
      相关资源
      最近更新 更多