In this week code review meeting, I open a discussion about testing code of Ruby on Rails. Actually I think the test for a complex system is not that easy if you wanna do it gracefully, but it’s still worth starting in small steps, that’s what’s important. I’d like to share the ideas people contributed, and the included contents are as following:
Andy:

Tried something new for RoR testing yesterday, it’s factory_girl(fixtures replacement solution. see more details). In this week code review, I show the testing code, and also have questions about test:

1. How to make sure that you test all(99.5%) situations.
2. If you test before you code, how to control/ensure the function points properly?

feel free to comment here if you have any thoughts on it.

I commented in front:

My understanding of TEST:
It means we should “program”(with mock and stub) one more time in different rule(based on the testing framework) to make sure features working correctly. but there is still a big chance to make mistake in both side(twice programming), so non-structured testing does not work very well. idea?

Then Guolei’s view point:

I don’t think writing test code is “twice programming”.
1. Test code should be very plain and very simple. Mock and stub do make it complex, but there’s rarely case requiring mock and stub. If you find your test code is more complex that function code, then you are going wrong way.
2. Actually writing test code can not be called “program”. It’s more like you are telling computer what it should do. It’s from the aspect of business logic, non with technique. So you should never make mistake when you get familiar with it, even so, it takes no time to fix it.
3. The value of test code shines when refactoring code and for complex logic. I think Andy’s code is not a good example because the logic is so simple that there’s more test code than function code.

Gary’s sample:

Your test code is for a process. I would rather test a method one time, I this case I can try all possible input and test.
for example:

describe "article?" do
    it "should figure out the page is article type" do
      2.times do
        @page.page_parts.create
      end
      @page.article?.should == true
    end
    it "should figure out the page is not article type" do
      2.times do
        @page.page_parts.create(:group_id => 0)
      end
      @page.article?.should == false
    end
  end

Then I posted:

Yeah.. I don’t doubt about the value of writing test code, and I totally agree on what GL mentioned should be the basic principle of adding test code.

The problem is, when I wanna cover all the situations/cases, there is, so far, no good way than writing lots of cases for it. so how to ensure it covering all the test-needed points, specially the business logic?
It’s not something about which skills(mock and stub) should be used somehow, it’s about what should be test and how to make sure you fully test what you want to test.

For example in my test code, how to ensure it can fully test the Item model? if I missed points, is this test still believable? I know surely it should cover all situations as what we want, but how to make the coverage happened correctly? The test code may save my time and energy when refactoring or doing enhancement, but if it’s not that correctly covering the case, does it still play an important role?

I am a bit confused of the way we are testing the system rather than writing test code for specific function, any more ideas?

JP’s response followed up:

As previously mentioned, I think its best to look at tests as a future investment, not a “I cover every point”. There are two types of “future investment”

1. Immediate future. You are about to write some code (function) and you know how you want it to work and what the different possibilities are. You write the test code first and then code the function. Now, as you code the function, it is much faster to see if it works (this is especially true for complex logic like the content inputting system).

2. Further future. You have a list of “known” situations/cases. You might modify the working code in the future and in the future you might forget what all these different situations/cases are – but you know what they are right now. So, you write test code. Now, if you modify the code in the future and the tests still pass, you can feel comfortable that the code you modified did not break any “known” conditions.
In both cases tests cannot find “unknown” cases…and I don’t think this is the point.

Two specific cases come to mind where I felt I used test code really well:
a) Immediate Future: When first starting the Erlang queue system I wrote a number of test cases to simulate the trainer client’s various conditions. This allowed me to write the erlang code and then run a bunch of cases on it really quickly which saved a lot of time (otherwise I would have had to do lots and lots of clicks on the Trainer Client). Unfortunately, once the queue system started integrating all of the other apps and connecting to FS I could no longer find a reasonable way to keep these test cases to work (as so many things would have to be stubbed).

a) Further future: The old, old carb.rb model. This was incredibly complex (even from the beginning). I wrote test code after writing that model. Then, a few months later, made significant changes to that model but was able to use that test code to insure I covered all the conditions (this was before Andy started working on it).

Seven’s response:

I’m only on the first step of writing test code, but it do helps me free my hands and ears from testing calls with FreeSWITCH. Nice.

My last response:

I think I got what I want from above discussion, thanks to all for sharing your view points or samples.

I tried factroy_girl yesterday to work with rspec framework, It’s excellent way to support testing, however, the polymorphic association in factroy_girl almost defeated me.
The factory definition in my code is as:

Factory.define :item do |i|
    i.status 0
    i.related_with_id 1
    i.association :unit
end

Factory.define :study_record do |sr|
    sr.association :content, :factory => :item
    sr.add_attribute :interaction_id
    sr.add_attribute :finished_at
end

Then I did this:


    @item = Factory(:item)
    @study_record = Factory(:study_record)
    @item.study_records << @study_record

But actually, this does not work to associate @study_record with @item, the problem is that @study_record.content is totally a new instance of Item rather than @item. It seems that "<<" doesn't work in this case.
The right way to do so:


     @item = Factory(:item)
    @study_record = Factory(:study_record, :content => @item)

Polymorphic association works, and it’s better than the fixtures solution indeed.

© 2011 Refactoring Thoughts Suffusion theme by Sayontan Sinha
普人特福的博客cnzz&51la for wordpress,cnzz for wordpress,51la for wordpress