CS312 - Spring 2012 - Class 5

  • administrative
       - assignment 1 grades back soon
       - assignment 2: how'd it go?
       - assignment 3
          - writing unit tests for assignment 2
             - you will be graded on how well your unit tests catch bad code
             - you will get brownie points if you're unit test catch problems in other people's code :)
          - will need to use git to track changes for your assignment
             - you will submit the commit log
       - assignment 2 grading will rely on assignment 3 unit tests
       - immediate feedback
          - some of these exercises push back assignment feedback
          - if you would like feedback sooner (or have particular questions about your assignment submission) come talk to me and we can look at your code together
       - reminder about computer use
  • Git recap
       - how did the git lab go?
       - git is a distributed version control system
          - contrast this with a centralized version control system
             - in this case there is a centralize version control server
             - all commits, etc. interact with the one server
             - if it's down, you can't use version control
          - for distributed version control, many local copies exist
             - often there is a single remote repository that acts as the production version, but that doesn't have to be the case
       - look at the diagram at: http://gitready.com/beginner/2009/01/21/pushing-and-pulling.html
          - most of what we looked at in the Git lab was working with a single local repository
             - you created a new repository
             - you added things to the index
             - you committed things from the index to your local repository
             - you looked at using diff, log and other tools to compare differences and look at the history
             - you looked at creating different branches
          - when you work in a team environment, there is often another layer, which is a remote repository
             - this could be a remote server that everyone is uploading to
             - or this could just be another local copy of someone else on the team
       - push and pull
          - Git can interact between repositories using the push and pull (or fetch) commands
          - to get started interacting with a remote repository, you first "clone" it using git clone
             - this will create a local repository with the same contents as the remote repository
          - if at any point you'd like to synch your local version with the remote version you can use fetch or pull to synch up
             - fetch updates your local repository with the contents of the remote repository
             - pull updates your workspace with the contents of the remote repository
             - note that in any of these cases, you could get conflicts which you would need to resolve
                - just like you can get conflicts when you try and merge branches
          - you can work away on your local repository until you're ready to move your changes to the remote repository
             - you can commit as many times as you want to your local repository and the changes will stay local
             - when you institute a "git push" it copies the contents from your local repository to the remote repository
             - if you run into a conflict, it will not let you commit unless your are up to date
                - for example, let's say you've pulled some version of the data, but since then, someone else has pushed some data
                - Git will NOT allow you to commit your changes because you're not up to date with the most recent version
                - to resolve this
                   - you'll need to do a fetch/pull to get the latest version
                   - you'll then need to resolve any conflicts
                   - then you can push your changes to the remote server (assuming someone else hasn't pushed again in front of you :)

  • Evaluating code
       - http://www.cs.middlebury.edu/~dkauchak/classes/cs312/handouts/code_eval.html
          - follow the instructions
          - this is both an educational exercise and will be partially used for grading
          - be clear, concise and constructive
          - when you're done, e-mail me your evaluation

  • coding style
       - how was it to read other people's code?
       - any challenges?
       - when you're writing code:
          - half of it is about getting it correct
          - the other half is about communicating to someone else reading your code
             - that may be you
             - or it may be other people
       - many of the conventions that we follow allow for better communication:
          - comments
          - variable, function, class naming
          - writing code in small functional units

  • testing code
       - Would you be confident telling someone that you have found all of the issues with the code?
          - put another way, if you didn't find any problems, would you be willing to tell someone that the code is correct?
       - if you found an error, did you know where the problem was?
       - Is this how you test your code? How do you test your code?
       - there are many ways of testing code and functionality (some better than others)

  • unit testings
       - there are a number of downsides to how you tested the code
          - may not have tested all of the functionality of the code
          - when you find a bug, it can be very hard to track down where the bug came from
       - unit tests are functions/programs/tests that check the functionality of your code
          - a "unit" is a single (generally small) functional chunk of the program
             - often it is a function or a few lines of code
          - unit tests are programs and can be run repetitively
          - they provide a concrete way of continuously testing your code
             - tests are written and confirmed to be working before moving on to other coding
             - all tests are run when new code is added, assuring that new code doesn't break old code
       - unit testing frameworks allow a lot of this to happen
          - organize tests
          - run them regularly
          - etc.

  • unit testing in Ruby
       - unit tests in Ruby are written as a separate class
          - the class MUST inherit from Test::Unit::TestCase
          - any method in this class that starts with "test" will be automatically run as a unit test (more on running unit tests soon)
          - inside the unit test you can assert that statements are true (or false)
             - if they are true, then the test passes
             - if the are not true, then the test fails
       - look at test_linked_list.rb in UnitTesting code
          - let's say we wanted to write some unit tests for the LinkedList class we've been using
          - we create a new class (commonly we'll also start the unit testing class name with Test)
          - the class should inherit from Test::Unit::testCase
             - you'll need to require "test/unit" at the top of your unit testing class
             - you'll also need to require the class/code you're trying to test
                - often we'll do require rather than require_relative since when it actually gets deployed, the relative paths may move around
          - looking at the LinkedList class
             - what are some of the functional units?
             - how can we test to make sure that they are working correctly?
          - add/remove
             - add some elements
             - remove them and make sure that we get back what we added
             - ideally, it would be nice to test these individually, but sometimes that can be hard, particularly when defining a class of objects
             - the test_add_and_remove method is a unit test
                - it must start with "test"
                - we use "assert_equal" to check if we obtain the expected output
                   - the first value is the value that we expect to get
                   - the second is the value of the thing we're testing
             - test_add_and_remove_better is another unit test
                - what does it do?
                - same as test_add_and_remove but does it using iteration
                   - we can use the range 1..10 to add the number 1 through 10
                   - 10.downto(1) allows us to iterate over the number 10 down to 1
                      - for each number, we have an assert to make sure that it has the right value
          - code organization
             - if we're testing right, we'll have one test class for each class/file
             - if we put these all in the same directory, things would tend to get cluttered
             - the most common convention is the following:
                - put all of the class files in a directory called "lib"
                - put all of the test files in a directory called "test"
          - running the unit tests
             - unit tests can be run just like any other program
                ruby test_linked_list.rb
             - because we inherited from Test::Unit::testCase the tests are automatically run with the class is defined
             - the only catch is that we used require rather than require_relative
                - we can add a directory where ruby looks for files (similar to the classpath in Java) using the -I command
             - so to run this test:
                ruby -I "lib" test/test_linked_list.rb
                (assuming we're at the base directory)
             - if we do that we see the following output:
                Run options:

                # Running tests:


                Finished tests in 0.000552s, 3623.1884 tests/s, 23550.7246 assertions/s.

                2 tests, 13 assertions, 0 failures, 0 errors, 0 skips

                - the last line tells us that:
                   - 2 tests were run
                   - 13 assertions were checked
                   - there weren't any errors or failures
                   - none of the tests were skipped
          - when tests don't work
             - let's pretend we made a mistake in the LinkedList::remove method
                - comment the linke that sets @head = @head.next, i.e. we forgot to actually remove the node
             - if we rerun the test
                Run options:

                # Running tests:


                Finished tests in 0.000782s, 2557.5448 tests/s, 5115.0895 assertions/s.

                 1) Failure:
                test_add_and_remove(TestLinkedList) [test/test_linked_list.rb:20]:
                <2> expected but was

                 2) Failure:
                test_add_and_remove_better(TestLinkedList) [test/test_linked_list.rb:30]:
                <9> expected but was

                2 tests, 4 assertions, 2 failures, 0 errors, 0 skips

                - for each assertion that failed, we get a printout
                   - which test failed
                   - which line in the test failed
                   - the answer we expected to get
                   - the answer we actually got
                - the summary shows 2 failed tests
                - notice that the first assertion that fails causes the whole test to fail and stop executing
                   - 4 assertions vs. 13 above