Your Federal Quarterly Tax Payments are due April 15th Get Help Now >>

Unit Testing in Ruby by yew20072

VIEWS: 0 PAGES: 20

									Unit Testing in Ruby




                       1-Jun-10
        Programming methodologies
   The grim facts:
       The majority of large programming projects fail
       Projects that succeed are usually full of bugs
       Modifying and debugging programs usually introduces yet more bugs
            Hence, it is hazardous to modify a “working” program
       The time spent maintaining a program far exceeds (10x?) the amount of
        time it took to write the program
   Programming methodologies are attempts to solve these problems
    by properly organizing programs and/or programmers
       The current (and best) methodologies are the “agile” methodologies
       “Agile” means writing software that is easily changed
       XP (Exteme Programming) is the best known agile methodology
       XP tends to work best for small groups of programmers
        Some ideas from agile programming
   There is no “silver bullet,” but agile methodologies are
    the best we have at present
   Most large programming projects fail, so...
       “Write the simplest thing that can possibly work.”
       Always have a working version, no matter how little it does
       Never add a feature until all known bugs have been fixed
       Any code that hasn’t been tested is assumed to be wrong
            Have tests for all code, and keep it up to date
            Run tests frequently—very frequently
            Tests must be trivially easy to run, or you won’t bother running them
        Test suites
   Obviously you have to test your code to get it working in the
    first place
       You can do ad hoc testing (running whatever tests occur to you at the
        moment), or
       You can build a test suite (a thorough set of tests that can be run at any
        time)
   Disadvantages of a test suite
       It’s a lot of extra programming
            This is true, but use of a good test framework can help quite a bit
       You don’t have time to do all that extra work
            False—Experiments repeatedly show that test suites reduce debugging time
             more than the amount spent building the test suite
   Advantages of a test suite
       Reduces total number of bugs in delivered code
       Makes code much more maintainable and refactorable
            This is a huge win for programs that get actual use!
        XP approach to testing
   In the Extreme Programming approach,
       Tests are written before the code itself
       If code has no automated test case, it is assumed not to work
       A test framework is used so that automated testing can be done after
        every small change to the code
            This may be as often as every 5 or 10 minutes
       If a bug is found after development, a test is created to keep the bug from
        coming back
   Consequences
       Fewer bugs
       More maintainable code
       Continuous integration—During development, the program always
        works—it may not do everything required, but what it does, it does right
        Terminology
   A test fixture sets up the data (both objects and primitives) that
    are needed to run tests
       Example: If you are testing code that updates an employee record, you
        need an employee record to test it on
   A unit test is a test of a single class
   A test case tests the response of a single method to a particular
    set of inputs
   A test suite is a collection of test cases
   A test runner is software that runs tests and reports results

   An integration test is a test of how well classes work together
       JUnit provides some limited support for integration tests
Once more, in pictures
test suite                               test runner
  another unit test
   test case (for one method)      A unit test tests the methods in a
   another test case                single class
                                   A test case tests (insofar as
  another unit test                 possible) a single method
   another test case                     You can have multiple test cases
   another test case                      for a single method
   another test case               A test suite combines unit tests
                                   The test fixture provides software
  unit test (for one class)
                                    support for all this
   test case (for one method)
                                   The test runner runs unit tests or
                                    an entire test suite
   another test case
                                   Integration testing (testing that it
                                    all works together) is not well
       test fixture                 supported by JUnit
         The test runner
   The test runner runs all your tests
       If they all succeed, you get a green bar




       If any fail, you get a red bar
        and links to the tests that failed
        How not to write a unit test
   Unit tests must be fast and easy to run—or you won’t run them
   The only output you should need to look at, in order to see that all
    tests passed, at is the green bar
       Of course, if you get a red bar, you need to explore further
   Don’t do any output from your unit tests!

   Ideally, the methods you are testing should not do any output
       In most well-written programs, there is a separation of concerns—methods
        either compute or do output, but not both
       It is possible to write unit tests for methods that do output, but that is a
        slightly advanced topic I won’t cover here
        How to write a unit test class
   A unit test class is a class you write that extends
    Test::Unit::TestCase
       You will need the line require 'test/unit'
   Your test class will inherit the following methods:
       def setup
            This a method that will be called before each of your test methods
            Typically, you will override this method and use it to assign values to
             some instance variables you need in testing
       def teardown()
            This a method that will be called after each of your test methods
            Typically you will just ignore this method, unless you need to close files
   You will also write any number of test methods, all of which
    have the form def test_Something
       Something is usually, but not necessarily, the name of the method you
        want to test
       Inside each test method, you will do some computations and call one or
        more assert methods to test the results
        Available assertion methods
   assert boolean
   assert_equal expected, actual
       Uses ==
   assert_same expected, actual
       Uses equal?
   assert_not_equal expected, actual
   assert_not_same expected, actual
   assert nil object
   assert not_nil object
   assert_block block
   All these methods can take an additional message argument
   This is not a complete listing of the assert methods
   The first two methods are by far the most commonly used
          Structure of a unit test
   require "test/unit"
    require "file_to_be_tested"
    class CountTest < Test::Unit::TestCase
     def setup
      # Perform initializations here
     end
     def test_some_method
      # Tests go here
     end
     def teardown
      # Release any resources (usually not needed)
     end
    end
        Testing for exceptions
   Methods should throw exceptions if they are called incorrectly
   You can test whether a method throws an exception when it
    ought to
        def test_exceptions
          begin
            # Call the method that should throw an exception
          rescue Exception # or you can test for specific exceptions
            return # The exception happened, so the test passes
          end
          flunk
       end
   Ruby also has assert_raise and assert_throws methods, but I
    haven’t been able to make them work
    A complete example
class Counter
 attr_reader :value    def reset
                        @value = 0
 def initialize        end
  @value = 0
 end                  end

 def increment *n
  if n == [ ]
    @value += 1
  else
    @value += n[0]
  end
 end
      The test class, part 1
require "test/unit"                      def test_increment_with_arg
require "counter"                         assert_equal 0, @c.value
                                          @c.increment 3
class CountTest < Test::Unit::TestCase    assert_equal 3, @c.value
                                          @c.increment 5
 def setup                                assert_equal 8, @c.value
  @c = Counter.new                       end
 end
                                         def test_reset
 def test_increment_with_no_args          @c.increment
  assert_equal 0, @c.value                @c.increment 10
  @c.increment                            assert @c.value > 0
  assert_equal 1, @c.value                @c.reset
  @c.increment                            assert @c.value.zero?
  assert_equal 2, @c.value               end
 end
    The test class, part 2
 def test_exceptions
  begin
    @c.increment 'four'
    return
  rescue Exception
  end
  flunk
 end

end # of the test class
         Test suites
   A test suite is a collection of unit tests
   In Ruby, all you have to do is require each test file
   Example suite (containing just the one unit test:
        require 'ruby_tests‘


   Note: In RadRails, running a test suite produces only text output
         Test-Driven Development (TDD)
   It is difficult to add unit tests to an existing program
        The program probably wasn’t written with testing in mind
   It’s actually better to write the tests before writing the code you
    want to test
    This seems backward, but it really does work better:
        When tests are written first, you have a clearer idea what to do when you
         write the methods
        Because the tests are written first, the methods are necessarily written to
         be testable
        Writing tests first encourages you to write simpler, single-purpose
         methods
        Because the methods will be called from more than one environment (the
         “real” one, plus your test class), they tend to be more independent of the
         environment
         Recommended approach
1.   Write a test for some method you intend to write
     •   If the method is fairly complex, test only the simplest case
2. Write a stub for the method
3. Run the test and make sure it fails
4. Replace the stub with code
     •   Write just enough code to pass the tests
5. Run the test
     •   If it fails, debug the method (or maybe debug the test); repeat until the
         test passes
6.   If the method needs to do more, or handle more complex
     situations, add the tests for these first, and go back to step 3
  The End
If you don't unit test then you aren't a software engineer, you are a
typist who understands a programming language.
                                           --Moses Jones


 1. Never underestimate the power of one little test.
 2. There is no such thing as a dumb test.
 3. Your tests can often find problems where you're not expecting them.
 4. Test that everything you say happens actually does happen.
 5. If it's worth documenting, it's worth testing.

                                                       --Andy Lester

								
To top