官术网_书友最值得收藏!

  • RSpec Essentials
  • Mani Tadayon
  • 676字
  • 2021-07-09 19:33:38

Using let and context

This scenario is very common, and inevitably will make the test harder to read, increasing the potential for misunderstanding the intent, which in turn will increase the likelihood of mistakes being made when the test or related code is changed. Before we see how to improve the test, we will learn about the let helper:

describe AddressValidator do
  let(:address) { {street: "123 Any Street", city: "Anytown"} }

  it "valid? returns false for incomplete address" do
    expect(AddressValidator.valid?(address)).to eq(false)
  end

  it "missing_parts returns an array of missing required parts" do
    expect(
      AddressValidator.missing_parts(address)
    ).to eq([:region, :postal_code, :country])
  end

  context "invalid characters in value" do

    let(:address){ {street: "123 Any Street", city: "Any$town%"} }
      
    it "invalid_parts returns keys with invalid values" do
      expect(
        AddressValidator.invalid_parts(address)
       ).to eq([:city])
      end
  end
end

The syntax is simple. The argument for the let helper is the name of the variable to be created, which you can reference within the same context as you would a local variable or function. In this case the argument is :address. The let helper also requires a block, that is is evaluated dynamically at runtime to provide the value for the object. In this case, we just supply a Hash for the value of address.

The secret to let is lambdas, or anonymous functions, which are evaluated at the moment they are called. Our first implementation doesn't show the power of lambdas. Using lambdas is what makes let so flexible and effective. Instead of a single, static let definition, we can create separate definitions for street and city, and reference them in address, allowing us to change inpidual parts of address as needed:

describe AddressValidator do
  let(:address) { { street: street, city: city } }
  let(:street)  { "123 Any Street"               }
  let(:city)    { "Anytown"                      }

  it "valid? returns false for incomplete address" do
    expect(AddressValidator.valid?(address)).to eq(false)
  end

  it "missing_parts returns an array of missing required parts" do
    expect(
      AddressValidator.missing_parts(address)
    ).to eq([:region, :postal_code, :country])
  end

  context "invalid characters in value" do
    let(:city) { "Any$town%" }

    it "invalid_parts returns keys with invalid values" do
      expect(
        AddressValidator.invalid_parts(address)
      ).to eq([:city])
    end
  end
end

Now we have a test case that clearly shows the differences in address, making the intent of the test case crystal clear. Using this pattern, we can add more cases with ease. We can change one or more nested values (for example city) or redefine address entirely:

describe AddressValidator do
  # notice that 'address' is defined as a Hash here  
  let(:address) { { street: street, city: city } }
  let(:street)  { "123 Any Street"               }
  let(:city)    { "Anytown"                      }

  it "valid? returns false for incomplete address" do
    expect(AddressValidator.valid?(address)).to eq(false)
  end

  context "address contains invalid characters" do
 # here we've redefined 'address' to be a String
 let(:address) { "$123% A^ St., Anytown, CA, USA 12345" }
    it "valid? returns false for incomplete address" do
      expect(AddressValidator.valid?(address)).to eq(false)
    end
  end

  context "address is a String" do
    let(:address) { "123 Any St., Anytown" }

    it "valid? returns false for incomplete address" do
      expect(AddressValidator.valid?(address)).to eq(false)
    end
  end

  context "complete address" do
     # we define 'address' as a Hash, but with all values 
 let(:address) do
 {
 street: "123 Any Street",
 city: "Anytown",
 region: "Anyplace",
 country: "Anyland",
 postal_code: "123456"
 }
 end

    it "valid? returns true" do
      expect(AddressValidator.valid?(address)).to eq(true)
    end

    context "address is a String" do
      let(:address) { "123 Any St., Anytown, CA, USA, 12345" }

      it "valid? returns true" do
        expect(AddressValidator.valid?(address)).to eq(true)
      end
    end
  end
end

We've just seen how let is a simple but effective tool for organizing tests, making them easy to understand and to maintain. We've also started using context in order to organize our tests. In fact, context is just an alias for describe. Often, the outermost grouping of RSpec examples is defined with describe and the inner groups are grouped using context, but both are different names for the exact same function. In the cases we have seen, context gives us a local scope where we can define different versions of our test inputs with let.

Now, let's move on to matchers, which give us a flexible way of making assertions.

主站蜘蛛池模板: 凤台县| 昌乐县| 太原市| 河间市| 五原县| 都昌县| 庆元县| 同德县| 肇庆市| 改则县| 淳安县| 紫金县| 府谷县| 隆尧县| 清涧县| 恩平市| 苍溪县| 刚察县| 太白县| 彭阳县| 高尔夫| 伊宁县| 民和| 清流县| 青冈县| 高清| 沙坪坝区| 盐山县| 牡丹江市| 昔阳县| 隆化县| 河北区| 丰顺县| 海兴县| 大埔县| 兖州市| 瑞安市| 财经| 盐山县| 阿合奇县| 霍林郭勒市|