简体   繁体   中英

Rails 4.2 - Update multiple attributes of a Factory Bot record after it is created

I have a Deal model with a model Step associated. a deal has_many Steps and a step belongs to a Deal.

I create a Deal Factory:

let!(:deal1)  { create(:deal, 
                                partner:          partner,                                
                                title:            "Deal1" ) }

For specific reasons related to validations and the use of the gem rspec-retry I can't create the associated Steps by using the "usual" after_create or Transients provided by Factory Girl, and it's Ok because my technique so far worked on all my tests. Her's what I do to create 5 associated Steps for Deal1:

  (0..4).each do |n|
        let!(:"deal1_step#{n}") {
          FactoryGirl.create(:step,
            order_nb: n,
            message_content: "this is message #{n}",        
           background_asset_filename: "campaigns/assets_for_tests/backgroundimage#{n}.jpg",  
            deal: deal)
        }
      end

This creates deal1_step0, deal1_step1, deal1_step2, deal1_step3 and deal1_step4, just as I need and it's working today on all my tests

My issue comes because I now must add to each deal1 steps attributes that differ and can't just be put in the code above as they are completely different from each other each time PLUS they only begin being present on step1 (step0 can't have a video). These attributes I need to send data to are:

  • video_url

  • video_x

  • video_y

1st attempt:

I tried the code below but it fails and gives me the error: deal1_step0.video_url = nil # there can't be any video on step0 (a validation is set for this)

`deal1_step1` is not available on an example group (e.g. a `describe` or `context` block). It is only available from within individual examples (e.g. `it` blocks) or from constructs that run in the scope of an example (e.g. `before`, `let`, etc).

deal1_step0.video_url = nil
deal1_step0.video_x = nil
deal1_step0.video_y = nil

deal1_step1.video_url = "https://www.facebook.com/rihanna/videos/10155221800656676/"
deal1_step1.video_x = 500
deal1_step1.video_y = 500

deal1_step2.video_url =  "https://www.facebook.com/ClaraLionelFoundation/videos/1821445664574948/"
deal1_step2.video_x = 500
deal1_step2.video_y = 300

deal1_step3.video_url  = "https://www.facebook.com/rihanna/videos/10155330511896676/"
deal1_step4.video_x = 250
deal1_step4.video_y = 500

2nd attempt:

I also tried something like but got a similar error like above: deal1_step1.update_attributes( video_url:

"https://www.facebook.com/rihanna/videos/10155221800656676/",
video_x: 500,
video_y: 500 )

3rd attempt:

I tried then to create sort of symbols:

  let(:video0) { nil }
  let(:video1) { "https://www.facebook.com/rihanna/videos/10155221800656676/" }
  let(:video2) { "https://www.facebook.com/ClaraLionelFoundation/videos/1821445664574948/" }
  let(:video3) { "https://www.facebook.com/rihanna/videos/10155330511896676/" }


(0..4).each do |n|
    let!(:"deal1_step#{n}") {
      FactoryGirl.create(:step,
        video_url: :"video#{n}",
        video_x: 500,
        video_y: 500,
        st_background_asset_filename: "campaigns/042017/assets_for_tests_and_dev/backgroundimage#{n}.jpg",  
        deal: deal1)
    }
  end

This time I got no error on the test that was able to be run BUT it does not work as the view thinks all those video_url are nil, it seems i don't manage to inject/update those attributes.

Is there a clean/proper way to update the attributes of deal1_step0, deal1_step1, deal1_step2, deal1_step3 and deal1_step4 ?

The easiest solution to this is to just define arrays of the changing values and use those in your FactoryGirl steps

urls = [nil, "https://www.facebook.com/rihanna/videos/10155221800656676/", ...]
xs = [nil, 500, ...]
ys = [nil, 200, ...]
(0..4).each do |n|
  let!(:"deal1_step#{n}") {
    FactoryGirl.create(:step,
      video_url: urls[n],
      video_x: xs[n],
      video_y: ys[n],
      st_background_asset_filename: "campaigns/042017/assets_for_tests_and_dev/backgroundimage#{n}.jpg",  
      deal: deal1)
  }
end

For anything else you need to understand how let / let! works. let defines a method (named by the symbol passed in) on the example (it's actually on a module that gets mixed in, same effect) which memoizes the response of the block passed to it. let! calls let and then defines a before block that calls the method let defined so it has already been run before the test example is. That's why you're getting the error ' deal1_step1 is not available on an example group (eg a ...', because the method is defined on the example, not on the group.

Therefore, if you don't want to do something like the arrays example above then you'd need to do your attribute updating in a block that gets run in the example context, so a before block - like

(0..4).each do |n|
    let!(:"deal1_step#{n}") {
      FactoryGirl.create(:step,
        order_nb: n,
        message_content: "this is message #{n}",        
       background_asset_filename: "campaigns/assets_for_tests/backgroundimage#{n}.jpg",  
        deal: deal)
    }
  end
before do 
  deal1_step1.video_url = "https://www.facebook.com/rihanna/videos/10155221800656676/"
  deal1_step1.video_x = 500
  deal1_step1.video_y = 500
  # may need to save deal1_step1 depending on exactly what you're looking for

  deal1_step2.update_attributes(video_url: "https://www.facebook.com/rihanna/videos/10155221800656676/", video_x: 500, video_y: 500 )

  ...
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM