Skittlish Tips'n'Tricks
- Change the classes for the body tag to set your default site style. also change these in the app.js file (line 66).
- Remove the scripts and the #options div if you don't want the option switcher.
- The top menu shows the home section and the sections that are not blogs.
- Email me at evil@che.lu if you have any questions.
- Happy hacking!
Mocking 'render :partial =>'
However, trying to implement the pattern we found that render_partial has been deprecated. We ended being able to do this:
describe "Showing a Readout" do
it "should render the readout partial to the page"
response.template.should_receive(:render).with(:partial => "readout").and_return("readout_partial_rendered")
render "/readouts/show"
response.should have_text("readout_partial_rendered")
end
end
Mocking partials allows for testing of views separately and increases test readability.
after(:expects) - Addressing BD stateless code
Rspec & Mocking stateless code leads to this:
before(:each) do @lime_in_the_coconut = "Mix it all together" @coconut_juice = "juice of the coconut" @lime_juice = "juice of the lime" @lime_mock = mock "lime" @coconut_mock = mock "coconut" @mixer_mock = mock "mixer" @juicer = Jucer.new(@mixer_mock) @lime_mock.should_recieve(:squeeze).and_return(@lime_juice) @coconut_mock.should_receive(:squeeze).and_return(@coconut_juice) @mixer_mock.should_receive(:mix).with(@lime_juice, @coconut_juice).and_return(@lime_in_the_coconut) end it "should return them mixed all together" do @juicer.mix_it_all_together(@coconut_mock, @lime_mock).should == @lime_in_the_coconut end
Adding after(:expects) could address the non-behavior driven test:
before(:all) do @lime_in_the_coconut = "Mix it all together" @coconut_juice = "juice of the coconut" @lime_juice = "juice of the lime" @lime_mock = mock "lime" @coconut_mock = mock "coconut" @mixer_mock = mock "mixer" @juicer = Jucer.new(@mixer_mock) end expect "it to squeeze the lime" do @lime_mock.should_recieve(:squeeze).and_return(@lime_juice) end expect "it to squeeze the coconut" do @coconut_mock.should_receive(:squeeze).and_return(@coconut_juice) end expect "it to mix the coconut and the lime juice" do @mixer_mock.should_receive(:mix).with(@lime_juice, @coconut_juice).and_return(@lime_in_the_coconut) end after(:expects) do @juice = @juicer.mix_it_all_together(@coconut_mock, @lime_mock) end it "should mix it all together" do @juice.should == @lime_in_the_coconut end
- before(:all) - Setup variables
- expects - Setup the mocks
- after(:expectations) - run the code after mocks are setup
- it/specs - normal expectations
- Teardown mock verifies - expectations are checked and fail if mock expectation were not satisfied
If our mock fails to verify the expect text could be raised giving you a better idea of what happened. Generally order does not matter for mocking but a expect(:first) parameter could be introduced to satisfy order critical testing
Rspec - context & state
describe "A GroupsController" do it "should let you create a new group and then redirect to the groups url and set the flash to 'Created new group'" do end endTesting a sword that has 2 directly related methods was a great example. When the methods do not relate is when the pattern breaks down, imho. I would argue that we are losing test clarity by keeping method names out of the context(s).
describe GroupsController, "#creating a valid group" do it "should create a new group" it "should redirect to the groups url" it "should set the flash to 'Created new group' endCalling a method can be a context. Starting a car for example is a state that cars can hold. I do agree that sharp? and rusty? are not states and putting them in the context was stretching the limits of that example.
Rspec Styles
describe "sharp? with a new sword" do
before(:each) do
@sword = Sword.new(:status => 'new')
end
it "should tell us that it is sharp" do
@sword.sharp?.should == true
end
end
describe "sharp? with a used sword" do
before(:each) do
@sword = Sword.new(:status => 'used')
end
it "should tell us that it is not sharp" do
@sword.sharp?.should == false
end
end
The describe/context when combined with the class name would become "Asking a new sword if it's sharp?". This style allows the sharp? methods to be clearly stated in a few areas. This still allows you to practice TDD. Each branch in "sharp?" gets its own context. If you see to many contexts for a certain method it's a good time to move functionality out and into another class, presenter or helper.
I tried Kurts idea for a while. But found I often became lost as to what describe/context should contain my specify/it. I then decided that the overhead of a few extra contexts what nothing compared to the benefit and clarity that my tests gained by putting the method names in the describe/context.
Mocking - What to test?
What do we want to test in this method?
class RecordCreator
def create_new_record(user, name)
record = Record.new
record.group = user.group
record.name = name
record.save!
end
end
With out mocks
def test_create_new_record_should_create_a_new_record_setting_user_group_and_saving record_creator = RecordCreator.new user = User.create!(:group => Group.new) record = record_creator.create_new_record(user, "My New Record's Name") assert_equal record.name, "My New Record's Name" assert_equal record.group, user.group assert !record.new_record? end
For some this might be fine. Either way there are a few things wrong with this test. The most obvious is we’re assuming we know everything the name= method does on record. What if it set the name and stripped out ’ replacing them with the html symbol. Our tests would then break.
- Hits the DB when it doesn’t have to, assuming save! and AR are defaults
- Tests name=, group= and user method on record
- Tests group and name getters on record
With Mocks
def test_create_new_record_should_create_a_new_record_setting_user_group_and_saving
user_mock = mock "user"
user_mock.stubs!(:group).and_return("The Group")
RecordCreator.should_receive(:new).and_return(record_mock)
record_mock.should_receive(:group=).with(user.group)
record_mock.should_receive(:name=).with("My New Record's Name")
record_mock.should_receive(:save!)
record = record_creator.create_new_record(user, "My New Record's Name")
#Go Start Test
assert_equal record, record_mock, "create_new_record should return back created record"
end
Now Why is this better?
We’re not hitting the DB
Your tests will be come faster.
Creating users isn’t our problem
If creating a users changes our test won’t break. Now the flip side if group ever goes away from user your tests will still pass but your code will break. Showing the importances of integration tests
name= can do what ever we want
The side effects of name=’s will no longer break our test.
One last thing
We used stub! for getting the group call out of user. I find stub! to be a perfect fit for just such an problem. First, you don’t have to worry how many times user.group was called. Second, you still prove that what ever user.group returned you used. Last, it cleans up your test! with(user.group) looks much better then with(@group) and one less @var has to be better.
Programmer costs
I find it to be a myth that businesses save on costs by forcing programmers to use their own computers. After watching distractions arise again and again it became clear to me that personal computers should be left out of the work place.
- IMing
- RSS readers
- Personal events calendar
- Personal Email (1 time an hour is enough)
- House keeping
- Downloads….
I’m not saying I don’t do this. Just the other day I
- Cleaned up the icons on my desktop
- Downloaded a new background
- Updated my gmail settings to download into Mail.app
- Read about the “Girls gone Wild” guy being arrested for saying “Judge gone wild” in court
I think pair programming suffers the most. I no longer feel as though I have the ability to continue if my pair leaves the room. I almost feel as though I have to stop looking at the code and move off the computer. You could argue that you shouldn’t be writing code with out a pair there but lets face it looking at a computer screen or problem and writing code are 2 different things.
If you’re going to force your programming staff to use their own computers at least have the common decency to buy them back up drives and pay the 80 dollars a year to insure their machines so that when they’re taking them on the subway to and from work they are protected. Possibly encourage alternative accounts if pairing and most of all clear your history I don’t need to see your fetishes.
The Rails of Demeter
Recently we’ve been moving our application to use rspec and more mock based testing. My boss constantly talks about code smells. Mocks allow you to get the stink out quick. As soon as it becomes a pain in the ass to test with a mock you know you’ve got something going on that’s bad. Lately I’ve found the biggest culprit is the Law of Demeter. Let’s review:
Law of Demeter with an object O should only access # O itself # M’s parameters # any objects created/instantiated within M # O’s direct component objects
As I started testing our view I was tricked. The first sign was the 5 NEW mocks that I had to create just to test roughly 10 lines of code. Which would be fine if the mocks weren’t chained:
a_mock.should_receive(:b).and_return(b_mock) b_mock.should_receive(:c).and_return(c_mock) c_mock.should_receive(:d)....
you get the point…
After pulling everything out I found this little turd..
@run.assay.readout_definitions.hit_definitions.color
Ugh!
Testing In Rails
All rails projects test something, test truth at the minimum. Reading through this post i saw this:
...does this testing make any sense? I mean, validation is a part of ActiveRecord, “which is pretty well tested already”
Testing 101
def sum(value_1, value_2) return value_1 + value_2 end def test_sum assert_equal sum(2,2), 4 end
What are you really testing?
We're not testing the '+' operator but rather everything around it. Think of validates_presents_of as your + operator. Now we need to test everything around it.
- You're testing that you spelled it correctly
- You're testing it works the way you say it does (+ takes 2 numbers)
- You're making yourself think before you code (not writing code you do not need)
- You're making others think before they remove (forcing a broken test or behavior if something is removed)
- You're making your job easier when DHH takes away validates_presents_of and replaces it with validates_syntax_sugar
Just like we REALLY don't care what the value is returned from by SUM so long as '+' happens. return value of "value_1 + value_2".
Mocking
If the sum method had to use another object to do the + I could then remove the result, or at least the logic of what 2+2 really is, from my test.
def sum(value_1, value_2) return Operators.add(value_1, value_2) end specify "sum should return the added values" do Operators.should_receive(:add).with(2, 2).and_return(5) sum(2, 2).should == 5 end
My test would no longer have to verify that "2 + 2" is really 4. It would instead be verifying that I wanted to return back from the method the result of "2 + 2" Because why would you test something that "is pretty well tested already”
Final Note
Another comment from this blog says: "I think testing simple validations is a bit much. I definitely don’t do it that much in OSS projects :) It’s just a judgment call you have to make I suppose. Do you have the time to test every single line?"
If you don't think you have the time to test every single line then maybe you shouldn't be writing that line. Because if you don't have time to write that line of code I'm positive you're not going to have the time to maintain it.
ActiveRecord.#{attribute}_before_type_cast
The problem:
A user enters an incorrect date into your form. After a submit the form returns back to with an error on the date field but their original value is gone.my_model.date_field = "tomorrow" #set some invalid date puts my_model.date_field #returns nil
The solution:
my_model.date_field_before_type_cast
Now instead of the user seeing a blank field they can see their original value with an error message. Letting them know that "tomorrow" is not a valid date.
my_model.date_field = "tomorrow" #set some invalid date puts my_model.date_field_before_type_cast #returns "tomorrow" as a string
Rails Delegate
NoMethodError: You have a nil object when you didn't expect it!
The error occurred while evaluating nil.hot
(__DELEGATION__):2:in `hot'
delegate :hot, :to => :cookieWhich should replace this:
has_a cookie def hot self.cookie.nil? ? nil : self.cookie.hot endBut instead falls a little short and would replace the following
def hot self.cookie.nil? ? nil.hot : self.cookie.hot end
Mac Book Pro Hard Drive Upgrade
The upgrade went well except for one small detail. Most hard drives have a small hole in the outer casing to allow the hard drive platters to breathe.
Hitachi just happens to have its hole conveniently placed in the same area where the Mac's blue tooth cable adheres itself to the hard drive.
As I gave up in my attempt to gain more slack on the bluetooth cable, I ignored the "Do not cover this hole" warnings, clearly marking the now well covered air hole. Like the beached whale Seinfeld episode: "Titleist.. Hole In One".
Hard Drive:
Hitachi Travelstar 5K160 HTS541616J9SA00