【问题标题】:Which behavior of this method I should test?我应该测试这种方法的哪种行为?
【发布时间】:2014-05-18 13:18:03
【问题描述】:

我想测试save方法:

class Note
  def initialize(password)
    @password = password
  end

  def save
    encryption = Note::Encryption.new(@password)
    encrypted   = encryption.encrypt(serialized)

    storage = Note::Storage.new

    storage.write(encrypted)
  end

  private
  def serialized
    {some_data: true}
  end
  # .....
end

方法只是主要将工作委托给其他类,这是唯一的责任。我测试它的第一个赌注只是检查:

  describe '#save' do
    let(:encryption){ '12345' }
    it 'calls encryption' do
      expect_any_instance_of(Note::Encryption).to receive(:encrypt)
      subject.save
    end

    it 'saves the file with data' do
      expect_any_instance_of(Note::Storage).to receive(:write)
      subject.save
    end
  end

我对这种方法有疑问,因为我有点担心这些测试不会测试太多......此外,现在的测试很高,加上实现而不是行为。有谁知道如何处理这种方法。值得一提的是,这个类将位于系统的顶部,因为它包装了一些资源。

【问题讨论】:

    标签: ruby-on-rails ruby rspec tdd sinatra


    【解决方案1】:

    我会测试更多,因为我想确定传递给这些方法的参数:

    class Note
      def initialize(password)
        @password = password
      end
    
      def save
        encrypted = Note::Encryption.new(@password).encrypt(serialized)
        Note::Storage.new.write(encrypted)
      end
    
    private
    
      def serialized
        {some_data: true}
      end
    end
    
    # the test
    describe '#save' do
      let(:password) { 'secret password' }
      let(:encryted) { 'encrytped string' }
      let(:storage) { double(:write => true) }
      let(:note_encryption) { double(:encrypt => encrypted) }
    
      subject(:note) { Note.new(password) }
    
      before do
        allow(Note::Encryption).to receive(:new).with(password).and_return(note_encryption)
        Note::Storage.stub(:new => storage)
      end
    
      it 'encrypted the password' do
        note.save
        expect(Note::Encryption).to have_received(:new).with(password)
        expect(note_encryption).to have_received(:encrypt).with(serialized) # mock serialized?
      end
    
      it 'stores the encryted string' do
        note.save
        expect(storage).to have_received(:write).with(encrypted)
      end
    end
    

    【讨论】:

    • 你打错了:.and_return(:note_encryption) 应该是.and_return(note_encryption),我还要在前面加上with(password)
    • 酷,我的想法很清楚,但令人困惑的是,在这种情况下代码和测试是如何耦合在一起的。接口的任何更改都可以说 Note::Encryption 将更改 2 个测试,Note::Encryption 本身和 Note。我认为如果输出和行为相同,尽管实现发生了变化,那么很酷的测试应该可以工作。你经常这样写代码吗?
    • 您现在可以删除have_received(:new),因为否则它不可能得到note_encryption(还有expect,而不是except...)
    • @LeszekAndrukanis:这取决于对你来说什么是重要的。如果是委托,请测试该方法是否将正确的参数委托给正确的方法。如果您对副作用和返回值更感兴趣,您可能希望与真正的 Encryption 对象进行集成测试,并仅测试 save 方法的返回值。
    【解决方案2】:

    如果您查看该方法,则会发现它正在做一些离散的事情:

    def save
      encryption = Note::Encryption.new(@password)
      encrypted   = encryption.encrypt(serialized) # first action
    
      storage = Note::Storage.new
    
      storage.write(encrypted) # second action
    end
    

    这些是你应该测试的东西,以及验证 - 如果@password 是一个空字符串会发生什么?

    测试可能看起来像这样:

    let(:password) { 'my_pass' }
    let(:note)     { Note.new(password) }
    let(:result)   { note.save }
    
    describe 'save' do 
      describe 'with a blank password' do
        let(:password) { '' }
    
        it 'fails' do 
          # assert_raises...
        end
      end
    
      it 'encrypts the password' do 
        refute_same password, result  #of course, this depends on what Storage.write returns...
      end
    
      it 'saves the password' do 
        # assert that the password is saved
      end
    
    end
    

    另外,如果我可以建议重构 -

    class Note
      def initialize(password)
        @password = password
      end
    
      def save
        encrypted = build_encrypted_object
        storage.write(encrypted)
      end
    
      private
      def build_encrypted_object
        encryption.encrypt(serialized)
      end
    
      def serialized
        {some_data: true}
      end
    
      def encryption
        Note::Encryption.new(@password)
      end
    
      def storage
        Note::Storage.new
      end
    
      # .....
    end
    

    【讨论】:

    • 它是如何让测试这个方法变得更容易的?验证超出了这项任务的范围,因为它是安静的理论
    • 不一定更容易测试,但更容易理解,更可重用和更清洁。最好是一种方法执行一项操作
    • 酷,我可以整晚都做 (gist.github.com/jaleszek/cb9390d2320e26ce75db) 但我很难测试这个而不是写干净。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-28
    • 1970-01-01
    相关资源
    最近更新 更多