programing

RSpec에서 모듈 테스트

elecom 2023. 6. 2. 20:07
반응형

RSpec에서 모듈 테스트

RSpec에서 모듈을 테스트할 때의 모범 사례는 무엇입니까?저는 몇 가지 모델에 포함된 몇 가지 모듈을 가지고 있으며, 현재는 각 모델에 대해 중복 테스트만 실시하고 있습니다(차이는 거의 없습니다).말릴 수 있는 방법이 있습니까?

래드웨이 =>

let(:dummy_class) { Class.new { include ModuleToBeTested } }

또는 모듈을 사용하여 테스트 클래스를 확장할 수 있습니다.

let(:dummy_class) { Class.new { extend ModuleToBeTested } }

앞에서 'let'을 사용하는 것이 인스턴스 변수를 사용하여 더미 클래스를 정의하는 것보다 좋습니다(:각).

RSpeclet()을 사용해야 하는 경우

마이크가 한 말.다음은 사소한 예입니다.

모듈 코드...

module Say
  def hello
    "hello"
  end
end

규격 조각...

class DummyClass
end

before(:each) do
  @dummy_class = DummyClass.new
  @dummy_class.extend(Say)
end

it "get hello string" do
  expect(@dummy_class.hello).to eq "hello"
end

분리하여 테스트하거나 클래스를 조롱하여 테스트할 수 있는 모듈의 경우, 저는 다음과 같은 것을 참조하십시오.

모듈:

module MyModule
  def hallo
    "hallo"
  end
end

사양:

describe MyModule do
  include MyModule

  it { hallo.should == "hallo" }
end

중첩된 예제 그룹을 가로채는 것은 잘못된 것처럼 보일 수 있지만, 저는 간결함이 좋습니다.무슨 생각 있어요?

저는 rspec 홈페이지에서 더 좋은 해결책을 찾았습니다.보아하니 공유 예제 그룹을 지원하는 것 같습니다.출처: https://www.relishapp.com/rspec/rspec-core/v/2-13/docs/example-groups/shared-examples!

공유 예제 그룹

공유 예제 그룹을 만들고 해당 그룹을 다른 그룹에 포함할 수 있습니다.

크고 작은 제품의 모든 에디션에 적용되는 동작이 있다고 가정합니다.

먼저, "공유" 동작을 고려합니다.

shared_examples_for "all editions" do   
  it "should behave like all editions" do   
  end 
end

그런 다음 대/소 버전에 대한 동작을 정의해야 할 경우 it_should_behave_like() 메서드를 사용하여 공유 동작을 참조합니다.

describe "SmallEdition" do  
  it_should_behave_like "all editions"
  it "should also behave like a small edition" do   
  end 
end

당신의 테스트 스크립트에 더미 클래스를 만들고 모듈을 거기에 포함시킬 수 있습니까?그런 다음 더미 클래스가 예상되는 방식으로 동작하는지 테스트합니다.

편집: 코멘트에서 지적한 것처럼 모듈이 혼합된 클래스에 일부 동작이 존재할 것으로 예상한다면, 저는 그러한 동작의 더미를 구현하려고 합니다.모듈이 자신의 임무를 수행하는 것을 행복하게 만들기에 충분합니다.

즉, 모듈이 호스트 클래스("호스트"라고 부릅니까?)에서 많은 것을 기대할 때 제 설계에 약간 신경이 쓰입니다. 기본 클래스에서 아직 상속되지 않았거나 상속 트리에 새 기능을 주입할 수 없다면 모듈이 가질 수 있는 모든 기대를 최소화하려고 노력할 것입니다.제가 걱정하는 것은 제 디자인이 불쾌한 융통성 없는 부분들을 발전시키기 시작할 것이라는 것입니다.

승인된 답변이 정답이라고 생각합니다만, rpsecs 사용 방법에 대한 예를 추가하고 싶습니다.shared_examples_for그리고.it_behaves_like방법들.코드 스니펫에서 몇 가지 요령을 언급하지만 자세한 내용은 이 relishapp-rspec-guide를 참조하십시오.

이를 통해 모듈이 포함된 모든 클래스에서 모듈을 테스트할 수 있습니다.즉, 애플리케이션에서 사용하는 것을 실제로 테스트하는 것입니다.

예를 들어 보겠습니다.

# Lets assume a Movable module
module Movable
  def self.movable_class?
    true
  end

  def has_feets?
    true
  end
end

# Include Movable into Person and Animal
class Person < ActiveRecord::Base
  include Movable
end

class Animal < ActiveRecord::Base
  include Movable
end

.movable_spec.rb

shared_examples_for Movable do
  context 'with an instance' do
    before(:each) do
      # described_class points on the class, if you need an instance of it: 
      @obj = described_class.new

      # or you can use a parameter see below Animal test
      @obj = obj if obj.present?
    end

    it 'should have feets' do
      @obj.has_feets?.should be_true
    end
  end

  context 'class methods' do
    it 'should be a movable class' do
      described_class.movable_class?.should be_true
    end
  end
end

# Now list every model in your app to test them properly

describe Person do
  it_behaves_like Movable
end

describe Animal do
  it_behaves_like Movable do
    let(:obj) { Animal.new({ :name => 'capybara' }) }
  end
end

가능한 한 하드 드라이브를 적게 사용하는 내 최근 작업

require 'spec_helper'

describe Module::UnderTest do
  subject {Object.new.extend(described_class)}

  context '.module_method' do
    it {is_expected.to respond_to(:module_method)}
    # etc etc
  end
end

좋겠다

subject {Class.new{include described_class}.new}

작동했지만 작동하지 않습니다(Ruby MRI 2.2.3 및 Rspec::코어 3.3.0)

Failure/Error: subject {Class.new{include described_class}.new}
  NameError:
    undefined local variable or method `described_class' for #<Class:0x000000063a6708>

descripted_class는 해당 범위에서 보이지 않습니다.

모듈을 테스트하려면 다음을 사용합니다.

describe MyCoolModule do
  subject(:my_instance) { Class.new.extend(described_class) }

  # examples
end

여러 사양에 걸쳐 사용하는 일부 항목을 건조하려면 다음과 같이 공유 컨텍스트를 사용할 수 있습니다.

RSpec.shared_context 'some shared context' do
  let(:reused_thing)       { create :the_thing }
  let(:reused_other_thing) { create :the_thing }

  shared_examples_for 'the stuff' do
    it { ... }
    it { ... }
  end
end
require 'some_shared_context'

describe MyCoolClass do
  include_context 'some shared context'

  it_behaves_like 'the stuff'

  it_behaves_like 'the stuff' do
    let(:reused_thing) { create :overrides_the_thing_in_shared_context }
  end
end

리소스:

다음은 어떻습니까?

describe MyModule do
  subject { Object.new.extend(MyModule) }
  it "does stuff" do
    expect(subject.does_stuff?).to be_true
  end
end

저는 더 크고 많이 사용되는 모듈의 경우 여기서 @Andrius가 제안한 "공유 예제 그룹"을 선택해야 한다고 제안합니다.여러 개의 파일이 있는 등의 문제를 겪고 싶지 않은 간단한 작업에 적합합니다.다음은 더미 물건의 가시성을 최대한 제어하는 방법입니다(rspec 2.14.6에 따라 코드를 복사하여 사양 파일에 붙여넣고 실행하기만 하면 됩니다).

module YourCoolModule
  def your_cool_module_method
  end
end

describe YourCoolModule do
  context "cntxt1" do
    let(:dummy_class) do
      Class.new do
        include YourCoolModule

        #Say, how your module works might depend on the return value of to_s for
        #the extending instances and you want to test this. You could of course
        #just mock/stub, but since you so conveniently have the class def here
        #you might be tempted to use it?
        def to_s
          "dummy"
        end

        #In case your module would happen to depend on the class having a name
        #you can simulate that behaviour easily.
        def self.name
          "DummyClass"
        end
      end
    end

    context "instances" do
      subject { dummy_class.new }

      it { subject.should be_an_instance_of(dummy_class) }
      it { should respond_to(:your_cool_module_method)}
      it { should be_a(YourCoolModule) }
      its (:to_s) { should eq("dummy") }
    end

    context "classes" do
      subject { dummy_class }
      it { should be_an_instance_of(Class) }
      it { defined?(DummyClass).should be_nil }
      its (:name) { should eq("DummyClass") }
    end
  end

  context "cntxt2" do
    it "should not be possible to access let methods from anohter context" do
      defined?(dummy_class).should be_nil
    end
  end

  it "should not be possible to access let methods from a child context" do
    defined?(dummy_class).should be_nil
  end
end

#You could also try to benefit from implicit subject using the descbie
#method in conjunction with local variables. You may want to scope your local
#variables. You can't use context here, because that can only be done inside
#a describe block, however you can use Porc.new and call it immediately or a
#describe blocks inside a describe block.

#Proc.new do
describe "YourCoolModule" do #But you mustn't refer to the module by the
  #constant itself, because if you do, it seems you can't reset what your
  #describing in inner scopes, so don't forget the quotes.
  dummy_class = Class.new { include YourCoolModule }
  #Now we can benefit from the implicit subject (being an instance of the
  #class whenever we are describing a class) and just..
  describe dummy_class do
    it { should respond_to(:your_cool_module_method) }
    it { should_not be_an_instance_of(Class) }
    it { should be_an_instance_of(dummy_class) }
    it { should be_a(YourCoolModule) }
  end
  describe Object do
    it { should_not respond_to(:your_cool_module_method) }
    it { should_not be_an_instance_of(Class) }
    it { should_not be_an_instance_of(dummy_class) }
    it { should be_an_instance_of(Object) }
    it { should_not be_a(YourCoolModule) }
  end
#end.call
end

#In this simple case there's necessarily no need for a variable at all..
describe Class.new { include YourCoolModule } do
  it { should respond_to(:your_cool_module_method) }
  it { should_not be_a(Class) }
  it { should be_a(YourCoolModule) }
end

describe "dummy_class not defined" do
  it { defined?(dummy_class).should be_nil }
end

도우미 유형을 사용할 수도 있습니다.

# api_helper.rb
module Api
  def my_meth
    10
  end
end
# spec/api_spec.rb
require "api_helper"

RSpec.describe Api, :type => :helper do
  describe "#my_meth" do
    it { expect( helper.my_meth ).to eq 10 }
  end
end

설명서는 다음과 같습니다. https://www.relishapp.com/rspec/rspec-rails/v/3-3/docs/helper-specs/helper-spec

당신은 당신의 스펙 파일에 당신의 모듈을 간단히 포함시킬 필요가 있습니다. mudule Test module MyModule def test 'test' end end end 파일에 . RSpec.describe Test::MyModule do include Test::MyModule #you can call directly the method *test* it 'returns test' do expect(test).to eql('test') end end

모듈 방법을 테스트하기 위한 하나의 가능한 솔루션으로, 모듈 방법을 포함하는 클래스에 독립적입니다.

module moduleToTest
  def method_to_test
    'value'
  end
end

그리고 그것에 대한 사양.

describe moduleToTest do
  let(:dummy_class) { Class.new { include moduleToTest } }
  let(:subject) { dummy_class.new }

  describe '#method_to_test' do
    it 'returns value' do
      expect(subject.method_to_test).to eq('value')
    end
  end
end

또한 DRY 테스트를 수행하려면 shared_examples를 사용하는 것이 좋습니다.

두 개 이상의 모듈을 테스트해야 하기 때문에 반복되는 패턴입니다.따라서 이를 위한 도우미를 만드는 것이 더 바람직합니다.

방법을 설명하는 이 게시물을 찾았지만 사이트가 언제 다운될지 몰라 대처하고 있습니다.

이는 객체 인스턴스가 인스턴스 메서드를 구현하지 않도록 하기 위한 것입니다. : 시도할 때 발생하는 오류는 무엇이든 상관없습니다.allow에 있는 방법.dummy학생들

코드:

spec/support/helpers/dummy_class_helpers.rb

module DummyClassHelpers

  def dummy_class(name, &block)
    let(name.to_s.underscore) do
      klass = Class.new(&block)

      self.class.const_set name.to_s.classify, klass
    end
  end

end

spec/spec_helper.rb

# skip this if you want to manually require
Dir[File.expand_path("../support/**/*.rb", __FILE__)].each {|f| require f}

RSpec.configure do |config|
  config.extend DummyClassHelpers
end

사양:

require 'spec_helper'

RSpec.shared_examples "JsonSerializerConcern" do

  dummy_class(:dummy)

  dummy_class(:dummy_serializer) do
     def self.represent(object)
     end
   end

  describe "#serialize_collection" do
    it "wraps a record in a serializer" do
      expect(dummy_serializer).to receive(:represent).with(an_instance_of(dummy)).exactly(3).times

      subject.serialize_collection [dummy.new, dummy.new, dummy.new]
    end
  end
end

언급URL : https://stackoverflow.com/questions/1542945/testing-modules-in-rspec

반응형