This post originated from an RSS feed registered with Ruby Buzz
by Jay Fields.
Original Post: Testing: Unit Test or Functional Test
Feed Title: Jay Fields Thoughts
Feed URL: http://blog.jayfields.com/rss.xml
Feed Description: Thoughts on Software Development
A common question when adding a new class is: Do I need a Unit test or Functional test? The answer: Yes.
I'm not sure why, but there seems to be a common misconception that if you have a Unit test a Functional test is not necessary. This simply is not true.
I believe it may stem from the days where we only had one test suite. In those days the entire suite was essentially a Functional test suite. However, we've learned many lessons since then, and some of those lessons have allowed us to create an additional test suite for testing the units of our system in isolation. And, while it's still true that sometimes you only need a Functional test, it's rarely true that you will only want a Unit test.
I love to talk about different strategies concerning Unit Testing. And, often during those discussions holes are pointed out where the unit tests will not fail despite non-functioning code. These holes appear at the class integration points, which are not exposed when testing classes in isolation. This is why creating Functional Tests is crucial to test driven development.
So, if we must write Functional tests anyway, what's the use in having Unit tests? I like to create Unit tests to verify edge cases and slightly different code paths. For example, imagine a phone number class that contains a method for returning formatted string representations of the phone number.
class PhoneNumber < ActiveRecord::Base def to_formatted_s type case type when :en then ... when :fr then ... end end end
The tests for the to_formatted_s method can be Unit tests that do not hit the database. It's possible to test all the scenarios required by to_formatted_s without the overhead of hitting the database. Avoiding trips to the database wont make a big difference if your entire test suite is 50 tests; however, as test suites grow, often so does the time to execute them.
If I were testing the PhoneNumber class I would create Unit tests for each logical path through the to_formatted_s method and I would create one Functional test that tested the same method, but actually used an instance of the PhoneNumber class that interacted with the database. This strategy would give me many Unit tests that quickly test the method and one Functional test that runs slower, but ensures integration is valid. Working this way should allow me to create a large test suite without creating a long running test suite.
You know you are in a bad situation when you don't want to run the tests because they take too long to execute. If you find yourself in this position, creating a test suite that cuts out trips to external dependencies (such as dbs or web services) is probably a good choice. The trips to the external dependencies are still required, but can be limited by testing as much logic as possible in the Unit tests and only hitting the external dependencies in the Functional tests.