[{"data":1,"prerenderedAt":111},["ShallowReactive",2],{"\u002Ftlt-2016.09.05-test-suite-debugging-time":3},{"id":4,"title":5,"body":6,"createdAt":98,"description":99,"draft":100,"extension":101,"image":102,"mastodonThread":102,"meta":103,"navigation":78,"path":104,"seo":105,"stem":106,"tags":107,"updatedAt":109,"__hash__":110},"content\u002FTLT - 2016.09.05 - Test Suite Debugging Time.md","TLT_-_2016.09.05_-_Test_Suite_Debugging_Time",{"type":7,"value":8,"toc":95},"minimark",[9,21,24,27,38,41,44,47,50,56,64,67,79,82],[10,11,12,13,20],"p",{},"One of the codebases I work on regularly has a large and slow test suite. So slow that it typically only runs in totality in a continuous-integration (ci) environment, and there it uses the ",[14,15,19],"a",{"href":16,"rel":17},"https:\u002F\u002Fgithub.com\u002Fgrosser\u002Fparallel_tests",[18],"nofollow","parallel_tests gem"," to slice it into pieces and run in parallel. This gets it to run in like 30 minutes instead of 2.5 hours.",[10,22,23],{},"But I want to run the whole thing linearly sometimes, especially so I can make sure tests aren't conflicting with each other. So I created a new ci job that runs it linearly ... and got a fail that I didn't get otherwise. Took forever to figure it out!",[10,25,26],{},"This was about a worker that I call a \"sweeper\" -- it's job is to run once a day and make sure nothing was missed by other realtime jobs during the day. Here is a simplified version.",[28,29,34],"pre",{"className":30,"code":32,"language":33},[31],"language-text","class SweeperWorker\n  INTERVAL = 25.hours.ago # Overlap the day by an hour\n  def perform\n    candidates.each(&:do_work) # something like that\n  end\n  def candidates\n    Thing.where('created_at > ?', INTERVAL)\n  end\nend\n\n# rspec\ndescribe SweeperWorker do\n  let(:new_thing) { Thing.new(created_at: 5.hours.ago) }\n  let(:old_thing) { Thing.new(created_at: 26.hours.ago) }\n\n  it \"does pick up the new thing\" do\n    new_thing # force to exist\n    expect(SweeperWorker.new.candidates).to include(new_thing)\n  end\n\n  it \"does not pick up the old thing\" do\n    old_thing # force to exist\n    expect(SweeperWorker.new.candidates).not_to include(old_thing)\n  end\nend\n","text",[35,36,32],"code",{"__ignoreMap":37},"",[10,39,40],{},"Not sure that is quite right, but you should get the idea.",[10,42,43],{},"The second example was failing -- both the \"old thing\" and the \"new thing\" were getting into the sweeper window -- but only when the test suite was run linearly. Run it in ci, no problem. Run just this test\u002Ffile locally, no problem.",[10,45,46],{},"The suspect is thus \"time\" -- but where? I went down the wrong track for a while, looking into timezones. Rails vs MySQL on ci vs my machine and so on ... some of those things weren't quite matched up, but still. I ran the test over and over, adding more and more debugging so I could peer into the contents of variables and the DB. Everything looked fine -- all the timestamps were what I expect. So it must be the query itself that is somehow asking for a longer timespan.",[10,48,49],{},"Finally I got it! Right there in the first line of SweeperWorker:",[28,51,54],{"className":52,"code":53,"language":33},[31],"INTERVAL = 25.hours.ago # Overlap the day by an hour\n",[35,55,53],{"__ignoreMap":37},[10,57,58,59,63],{},"This constant is set ",[60,61,62],"b",{},"when the class is loaded",", and never changed! So by the time this test runs in the 2.5 hours of test time, \"25 hours ago\" is now \"27.5 hours ago\" -- so our \"old\" thing is in our window. The \"25.hours.ago\" certainly looks relative, but it is a calculation that outputs an absolute time which is then saved for later use.",[10,65,66],{},"Move that constant into the method as a variable and it is fixed. And now we can sing our victory song!",[10,68,69,70],{},"[",[71,72],"iframe",{"width":73,"height":74,"src":75,"frameBorder":76,"allow":77,"allowFullScreen":78},560,315,"https:\u002F\u002Fwww.youtube.com\u002Fembed\u002Fw8KQmps-Sog](\u002Fyoutube:w8kqmps-sog)","0","accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture",true,[10,80,81],{},"Lessons:",[83,84,85,89,92],"ul",{},[86,87,88],"li",{},"Beware of calculated constants",[86,90,91],{},"Make sure relative values are actually relative",[86,93,94],{},"If the speed of execution (slow vs fast) changes the outcome, look for things that are more or less spread out in the timeline, such as constant definition.",{"title":37,"searchDepth":96,"depth":96,"links":97},2,[],"2016-09-05T16:07-04:00","One of the codebases I work on regularly has a large and slow test suite. So slow that it typically only runs in totality in a continuous-integration (ci) environment, and there it uses the parallel_tests gem to slice it into pieces and run in parallel. This gets it to run in like 30 minutes instead of 2.5 hours.",false,"md",null,{},"\u002Ftlt-2016.09.05-test-suite-debugging-time",{"title":5,"description":99},"TLT - 2016.09.05 - Test Suite Debugging Time",[108],"blog","2016-09-05T18:01-04:00","MiYUTVw9kv4C-HjFL8h0DxjzmIUrq4U1SPO0U9KApJw",1778988314572]