Rspec、模型加载顺序、固定装置和named_scope 挑战

发布于 2024-08-10 11:35:43 字数 772 浏览 4 评论 0原文

我有一些球员,球员有交易状态。我认为我会很聪明并拥有一个单独的 TradeState 模型,以便玩家拥有 trade_state_id (玩家只能处于一次一种贸易状态)。

现在,通过使用命名范围然后通过说“Player.active”来获取所有活跃玩家将很方便。为此,我需要获取与“active”匹配的 TradeState 记录的 ID,因此我在 Player 类中提出了这个:

named_scope :active, :conditions => {:trade_state_id => TradeState.active.first.id}

在脚本/控制台中测试时,这就像一个魅力,但当我去测试。我正在使用 RSpec,但我怀疑这不相关。当我运行最简单的测试时,我收到以下错误:

“为 nil 调用 id,这会错误地为 4”

据我所知,测试框架正在按字母顺序加载和解析模型。该框架解析 Player 模型中的 name_scope 调用,并尽职尽责地查找第一个处于活动状态的 TradeState 记录的 id。然而,该模型尚未处理且尚未准备好,因此会出现有关获取 nil id 的错误。

起初我以为是因为 trade_states 表中可能没有任何记录,所以我在 before(:each) 块中创建并保存了我需要的 trade_states,但这不起作用。然后我制作了一些装置并尝试加载它们,但这不起作用。

这看起来合理吗?还有其他解释吗?解决方法怎么样?我可以尝试模拟 TradeState 对象,然后我会尝试一下。

非常感谢您抽出时间。

I have some players and the players have a trade state. Rather than hard code trade states like "active" and "inactive" and then have to go looking for strings, I thought I'd be clever and have a separate TradeState model so that a Player has a trade_state_id (a Player can be in only one trade state at a time).

Now, it would be a convenience to be able to get all the active players by using named scopes and then by saying "Player.active". To do that, I need to get the ID of the TradeState records that matches 'active', so I came up with this in the Player class:

named_scope :active, :conditions => {:trade_state_id => TradeState.active.first.id}

This works like a charm when tested in script/console, but it does not work when I go to test. I'm using RSpec, but I suspect that isn't pertinent. When I run the most trivial test, I get the following error:

"Called id for nil, which would mistakenly be 4"

As far as I can tell, the testing framework is loading and parsing the models in alphabetical order. The framework parses the named_scope call in the Player model and dutifully goes to look up the id for the first TradeState record that is active. However, that model hasn't been processed yet and isn't ready, hence the error about getting the id of nil.

At first I thought it was because there may not have been any records in the trade_states table, so I create and save the trade_states that I needed in the before(:each) block, but that didn't work. So then I made some fixtures and tried loading them, but that didn't work.

Does this seem plausible? Are there other explanations? How about work arounds? I could try mocking the TradeState object and I'll give that a go.

Thanks so very much for your time.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

娜些时光,永不杰束 2024-08-17 11:35:43

您提到的这段代码在播放器“加载”时执行。因此,即使您在 before(:each) 块中创建设置数据,该模型也可能在此之前加载。要解决该问题,您可以尝试以下操作:

named_scope :active, lambda { {:conditions => {:trade_state_id => TradeState.active.first.id} } }

这可确保每当您调用named_scope时都会调用TradeState.active。

但是通过调用 TradeState.active.first 可以实现什么目的。 做到这一点:

:conditions => {:trade_state_id => TradeState::ACTIVE}

相反,您可以通过这种很好的方式在 TradeState 模型上使用以下代码来

class TradeState < ActiveRecord::Base
  def self.const_missing(sym)
    const_set(sym, find_by_name(sym.to_s))
  end
end

This piece of code you have mentioned gets executed when Player is "loaded". So even if you create the setup data in before(:each) block, this model might have loaded even before that. To fix the problem you can try some thing like:

named_scope :active, lambda { {:conditions => {:trade_state_id => TradeState.active.first.id} } }

This ensures that the TradeState.active is called whenever you call the named_scope.

But what are you achieving by calling TradeState.active.first. Instead you can do so by this nice way

:conditions => {:trade_state_id => TradeState::ACTIVE}

with the following code on your TradeState model:

class TradeState < ActiveRecord::Base
  def self.const_missing(sym)
    const_set(sym, find_by_name(sym.to_s))
  end
end
叫思念不要吵 2024-08-17 11:35:43

这实际上是一个潜在的“预期”结果。如果没有活跃玩家,则

TradeState.active

为空,因此

TradeState.active.first

为 nil,

从而

TradeState.active.first.id

在 nil 对象上调用 Object::id

您可以尝试:

TradeState.active.empty? ? 0 : TradeState.active.first[:id]

在任何情况下,请确保包含 trade_states 以确保它们存在于连接中。

This is actually a potentially "expected" outcome. If you have no active players, then

TradeState.active

is empty, and thus

TradeState.active.first

is nil,

and thus

TradeState.active.first.id

calls Object::id on a nil object.

You might try:

TradeState.active.empty? ? 0 : TradeState.active.first[:id]

In any case, be sure you include trade_states just to make sure they are present for the join.

风流物 2024-08-17 11:35:43

不幸的是,尽管所有这些建议都非常好,但在运行测试用例时没有任何可靠的工作。起初,我放弃了所有这些代码,但我必须编写的代码吸引力太低,以至于我开始重新思考这个问题。

因为我们将通过大量的交易状态来查找玩家,所以在数字交易状态列上建立索引是一个明确的优先事项。然而,没有任何内容表明贸易状态需要是一个表格,因为只定义了几个贸易状态,并且定义的贸易状态不太可能有太大的波动。

因此,我使 TradeState 成为一个独立的类,不再从 ActiveRecord 派生,并定义了我需要的常量。

感谢大家的想法和解决方案。

Unfortunately, although all these suggestions were very good, nothing worked reliably when running test cases. At first I backed out all this code, but the code that I had to write was so much less attractive that I started to rethink the problem.

Because we will be looking up players by their trade state a great deal, having an index on a numerical trade state column is a clear priority. However, there is nothing that said that trade state needed to be a table, since there are only a few trade states defined and it is unlikely that there will ever be much volatility in defined trade states.

Therefore, I made TradeState a class in its own right, no longer derived from ActiveRecord, and defined the constants that I needed.

Thank you all for your thoughts and solutions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文