我们正在从我们构建的名为DBQuery的gem重构一个名为DataSourceIntegrations的
Ruby应用程序.我正在将一些DBQuery代码迁移到DataSourceIntegrations中.我正在构建的部分取决于DBQuery,它将在单独的步骤中添加.
同时,我需要编写RSpec测试来验证是否正确调用了DBQuery代码,所有这些都没有DBQuery.
我有的是:
码-
宝石代码 –
module DBQuery class Query MAX = 1000 def retrieve_users # Returns an array of user IDs end end end
申请代码 –
module Integration def initialize @query = DBQuery::Query.new end end module Integration class StackOverflowIntegration include Integration def query users = [] while (users < DBQuery::Query::MAX) do # Creates a users buffer users.push @query.retrieve_users(users_buffer) end end end end
测试─
describe Integration::StackOverflowIntegration do let(:db_query) { double('DBQuery::Query') } before do stub_const('DBQuery::Query::MAX',1000) allow(db_query).to receive(:new).and_return(db_query) allow(db_query).to receive(:retrieve_users).and_return([1000,1001,1002]) end it 'queries without error' do expect { StackOverflowIntegration.new.query }.to_not raise_error end end
我无法弄清楚如何以不需要DBQuery的方式存根.我的错误是:
NoMethodError: undefined method `new' for #<Module:0x007fa7ce561968>
我不知道为什么DBQuery :: Query被表示为模块,或者如何让它识别“new”.
解决方法
据我所知,您希望对DBQuery :: Query有所期望,而无需在代码中定义它. rspec-mocks可以
stub an undefined constant,就像你为DBQuery :: Query :: MAX所做的那样.要完全存根DBQuery :: Query,首先创建一个
class double并在测试中为它创建一个const:
db_query__query_class = class_double('DBQuery::Query') stub_const('DBQuery::Query',db_query__query_class)
这样,代码中的DBQuery :: Query将返回query_class double.然后你可以用它定义一些行为:
query_instance = instance_double('DBQuery::Query') allow(db_query__query_class).to receive(:new).and_return(query_instance) allow(db_query).to receive(:retrieve_users).and_return([1000,1002])
您仍然需要存根嵌套常量,如DBQuery :: Query :: MAX
stub_const('DBQuery::Query::MAX',1000)
关于风格,我更喜欢放置存根并允许let / let!像这样的陈述:
describe Integration::StackOverflowIntegration do let!(:db_query__query_class) do class_double('DBQuery::Query').tap do |double| stub_const('DBQuery::Query',double) stub_const('DBQuery::Query::MAX',1000) allow(double).to receive(:new).and_return(query_instance) end end let(:query_instance) do instance_double('DBQuery::Query').tap do |double| allow(double).to receive(:retrieve_users).and_return([1000,10001,1002]) end end end
此外,我喜欢将返回的值放在自己的let中,以便我可以轻松地更改它们.这是一个完整的工作(和虚拟)示例:
RSpec.configure do |c| c.around(:context,:protect_with_timeout) do |example| Timeout::timeout(2) { example.run } end end describe Integration::StackOverflowIntegration do let!(:db_query__query_class) do class_double('DBQuery::Query').tap do |double| stub_const('DBQuery::Query',max_queries) allow(double).to receive(:new).and_return(query_instance) end end let(:query_instance) do instance_double('DBQuery::Query').tap do |double| allow(double).to receive(:retrieve_users).and_return(retrieved_users) end end let(:max_queries) { 1000 } let(:retrieved_users) { [1000,1002] } describe '#query' do subject(:stack_overflow_query) { Integration::StackOverflowIntegration.new.query } it 'queries without error in nominal case' do expect { stack_overflow_query }.to_not raise_error end context 'with 0 users returned' do let(:retrieved_users) { [] } it 'does not loop forever',:protect_with_timeout do pending('not implemented yet...') stack_overflow_query # will timeout end end context 'with 10 users returned' do let(:retrieved_users) { [1,2,3,4,5,6,7,8,9,10] } it 'calls #retrieve_users 100 times' do stack_overflow_query expect(query_instance).to have_received(:retrieve_users).exactly(100).times end end context 'with DBQuery::Query::MAX set to 0' do let(:max_queries) { 0 } it 'does not call #retrieve_users at all' do stack_overflow_query expect(query_instance).not_to have_received(:retrieve_users) end end end end
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。