我有一个类,在一种情况下应调用:my_method
,但在另一种情况下则不得调用方法:my_method
。我想测试两种情况。另外,我希望测试记录不应该调用:my_method
的情况。
Using any_instance
is generally discouraged,所以我很乐于学习替换它的好方法。
此代码段是我想编写的那种测试的简化示例。
class TestSubject
def call
call_me
end
def call_me; end
def never_mind; end
end
require 'rspec'
spec = RSpec.describe 'TestSubject' do
describe '#call' do
it 'calls #call_me' do
expect_any_instance_of(TestSubject).to receive(:call_me)
TestSubject.new.call
end
it 'does not call #never_mind' do
expect_any_instance_of(TestSubject).not_to receive(:never_mind)
TestSubject.new.call
end
end
end
spec.run # => true
它可以工作,但是使用expect_any_instance_of
方法,不推荐使用。
如何更换?
答案 0 :(得分:3)
我会那样做
describe TestSubject do
describe '#call' do
it 'does not call #something' do
subject = TestSubject.new
allow(subject).to receive(:something)
subject.call
expect(subject).not_to have_received(:something)
end
end
end
希望这对您有帮助!
答案 1 :(得分:1)
这是我通常进行单元测试的方式。我更新了代码以支持您(或其他读者)将来可能遇到的其他可能的问题。
class TestSubject
def call
some_call_me_value = call_me
call_you(some_call_me_value)
end
def call_me; end
def call_you(x); end
def never_mind; end
class << self
def some_class_method_a; end
def some_class_method_b(x, y); end
end
end
require 'rspec'
spec = RSpec.describe TestSubject do
context 'instance methods' do
let(:test_subject) { TestSubject.new }
describe '#call' do
let(:args) { nil }
let(:mocked_call_me_return_value) { 'somecallmevalue' }
subject { test_subject.call(*args) }
before do
allow(test_subject).to receive(:call_me) do
mocked_call_me_return_value
end
end
it 'calls #call_me' do
expect(test_subject).to receive(:call_me).once
subject
end
it 'calls #call_you with call_me value as the argument' do
expect(test_subject).to receive(:call_you).once.with(mocked_call_me_return_value)
subject
end
it 'does not call #never_mind' do
expect(test_subject).to_not receive(:never_mind)
subject
end
it 'calls in order' do
expect(test_subject).to receive(:call_me).once.ordered
expect(test_subject).to receive(:call_you).once.ordered
subject
end
end
describe '#call_me' do
let(:args) { nil }
subject { test_subject.call_me(*args) }
# it ...
end
describe '#call_you' do
let(:args) { nil }
subject { test_subject.call_you(*args) }
shared_examples_for 'shared #call_you behaviours' do
it 'calls your phone number'
it 'creates a Conversation record'
end
# just an example of argument-dependent behaviour spec
context 'when argument is true' do
let(:args) { [true] }
it 'does something magical'
it_behaves_like 'shared #call_you behaviours'
end
# just an example of argument-dependent behaviour spec
context 'when argument is false' do
let(:args) { [false] }
it 'does something explosive'
it_behaves_like 'shared #call_you behaviours'
end
end
end
context 'class methods' do
let(:args) { nil }
describe '#some_class_method_a' do
let(:args) { nil }
subject { TestSubject.some_class_method_a(*args) }
# it ...
end
describe '#some_class_method_b' do
let(:args) { [1, 2] }
subject { TestSubject.some_class_method_b(*args) }
# it ...
end
end
end
spec.run # => true
答案 2 :(得分:0)
不要测试是否调用了某种方法。
这会将您的测试限制在实现细节上,并且每次您重构被测类时都将迫使您更改测试(更改实现细节而不更改行为)。
代替测试返回值或更改的应用程序状态。
很难拿出示例,因为您没有提供有关所测试类的足够上下文。
class CreateEntity
def initialize(name)
@name = name
end
def call
if company_name?(@name)
create_company
else
create_person
end
end
def create_person
Person.create!(:name => @name)
end
def create_company
Company.create!(:name => @name)
end
end
# tests
RSpec.describe CreateEntity do
let(:create) { CreateEntity.new(name).call }
describe '#call' do
context 'when person name is given' do
let(:name) { 'Firstname Lastname' }
it 'creates a person' do
expect { create }.to change { Person.count }.by(1)
end
it 'do not create a company' do
expect { create }.not_to change { Company.count }
end
end
context 'when company name is given' do
let(:name) { 'Name & Sons Ltd' }
it 'creates a company' do
expect { create }.to change { Company.count }.by(1)
end
it 'do not create a person' do
expect { create }.not_to change { Person.count }
end
end
end
end
通过上述测试,我将能够更改CreateEntity.call
方法的实现方式,而无需改变测试,只要行为保持不变即可。