Feeds:
Posts
Comments

Posts Tagged ‘refactoring’


In part two I’ll keep testing the prime? method and then I’ll make a new method that uses the same algorithm and that way show some refactoring.

Let’s start by checking for faulty input. A prime number can’t be negative so there’s no use checking for primes in values below zero. It might seem harsh but I decided to throw an exception when the user tries to input a negative number into my prime method. For this I’ll use the ArgumentError that comes with standard Ruby. I could create my own NegativeArgumentError (which is quite easy) if I wanted but that’s beside the focus of this post.

So, as usual I start with writing the test first.

def test_negative_input_to_prime_raises_exception
    assert_raises(ArgumentError) {@primality_tester.prime?(-2)}
end

Here I tell the framework that I am expecting the function to raise an exception by the name ArgumentError when I send in a negative value.

Running this I get a failed test. ArgumentError is not thrown, instead a DomainError is thrown from the sqrt-function (if you know your math you know that you can’t do the square root of negative values unless you use complex numbers). We’ve reached the red phase, test fails. Now, let’s make it pass.

Ok, simplest solution first.

def prime?(num)
    raise ArgumentError if num == -2
    
    # More code here
  end
end

Run tests and they pass.
Ok, next test, another negative number. Let’s say -23.
Tests fail.
And now I’ll make sure to plug that hole.

def prime?(num)
    raise ArgumentError if num > 0
    
    # More code here
  end
end

Tests pass.
Do I need to test more negative numbers? No, not really. I know that I got the > right as it takes negative numbers and not positive and there’s no real edge cases to test (not entirely true, zero would be an edge case but I’ll test zero later) so I’ll be happy and done with negative numbers.

And finally there’s a few edge cases aswell that I want to test. These being 1 and 0. Per definition 1 is not prime eventhough it can only be divided by 1 and itself. Let’s make sure 1 is not considered prime.

Test first and make sure it fails.

def test_1_is_not_detected_as_prime
    refute( @primality_tester.prime?(1), "1 should not be considered prime")
end

Let’s make it pass now.

def prime?(num)
  raise ArgumentError if num < 0
  return true if num == 2
  return false if num % 2 == 0 or num == 1

  # More code after this
end

For the observant of you, you’ll notice that I did a small refactoring here.
Before I returned true if num % 2 was not zero. Now I changed that into returning false if num % 2 is zero, I also tacked on 1 at the end. All my tests pass so the refactor did not break anything.

And the last test on the prime? method. 0. 0 is not prime and should thus return false.

When I run the test for 0 I get pass from the start, no failed test there. Why? Well, let’s look at the code again.

def prime?(num)
  raise ArgumentError if num < 0
  return true if num == 2
  return false if num % 2 == 0 or num == 1

  # More code here
end

0 divided by 2 is 0. So our case of 0 is caught by line 4 already. Alright, since I could verify that so easily I’ll not make it fail before I again make it pass.

Am I done?
Not quite. Now I know that the function detects primes fine, it raises an exception for negative input and the edge cases 0 and 1 are not considered prime. But, what about non primes, does it really return false for non primes? For all even it does but what about 9, 15, 21, 51? I’ll make a test covering that aswell. To make sure even are not detected prime I’ll also put 4 in the list.

[4, 9, 15, 21, 51].each do |p|
  define_method :"test_that_#{p}_is_not_detected_as_prime" do
    refute( @primality_tester.prime?(p), "Expected false, got true")
  end
end

And those tests pass aswell.

Are there any more tests that could be done here? There might be a few but I’m quite happy with the ones that are already in there. Now I know that the function raises exceptions for negative input, that it gives me correct answers for values prime and not prime, and I’ve tested the edge cases.

Read Full Post »


So, to continue* the series lets keep working on the prime calculator.

* – I asked a question on stackoverflow yesterday and got some good tips. It also seems that I did not follow The Ruby Way of writing and did not really follow the DAMP-principle. So, I’ll do a small recap and explain the points that were made in the answer to my question. Read the previous post for more on DRY and DAMP principles.

I’ll do a small recap of last post showing the changes that were proposed and what they mean.

The proposed changes was this:

Instead of having one test function for each test case we can loop over the values we want to test.

def test_that_the_first_few_primes_are_detected_as_prime
  [2, 3, 5, 7, 11, 13, 17].each do |p|
    assert( @primality_tester.prime?(p), "expected true, received false" )
  end
end

The result is not optimal from my point of view:

F:\temp>ruby test\test_prime_calculator.rb
Loaded suite test/test_prime_calculator
Started
....F.
Finished in 0.002001 seconds.

  1) Failure:
test_that_the_first_few_primes_are_detected_as_prime(TestPrimeCalculator) [test/test_prime_calculator.rb:41]:
Expected true, got false

6 tests, 11 assertions, 1 failures, 0 errors, 0 skips

Test run options: --seed 18437

As you can see there are no indications of what went wrong. We have no idea which number was not prime.

Enter second method:

[2, 3, 5, 7, 11, 13, 17].each do |p|
  define_method :"test_that_#{p}_is_detected_as_prime" do
    assert( @primality_tester.prime?(p), "expected true, received false" )
  end
end

So, we get into the hardcore stuff and do some meta programming. Here we loop over the prime numbers we want to test and define a function for each of them. The failure message is much better. Now we know that 2 was not returned as a prime.
This might seem a little overkill for the problem that we want to solve.

  1) Failure:
test_that_2_is_detected_as_prime(TestPrimeCalculator) [test/test_prime_calculator.rb:47]:
Expected true, got false

Maybe the best was to keep the way of testing that we had when we started (just some refactoring, using assert and changing the names a little):

def test_that_2_is_detected_as_prime
  assert @primality_tester.prime?(2)
end

def test_that_3_is_detected_as_prime
  assert @primality_tester.prime?(3)
end

def test_that_5_is_detected_as_prime
  assert @primality_tester.prime?(5)
end

Or we could make it even more compact:

def test_that_the_first_few_primes_are_detected_as_prime
  assert @primality_tester.prime?(2)
  assert @primality_tester.prime?(3)
  assert @primality_tester.prime?(5)
end

I’m not really happy with the last way however. My suggestion would be the meta-programming way of defining functions as the tests run or if the tests are very simple then doing a separate function for each input to test. That can be structured into other files if we don’t want the clutter that comes from it. That file holds all the tests for checking valid prime numbers.

Read Full Post »


I quite recently started unit testing and as a first stop I played a bit with Ruby and Unit::Test as those are so easy to get going with. In C# you have to download nUnit and configure that, set up a lot of projects and references. Now, I’m not saying that is hard – I’m just saying that it’s easier and quicker to do it in Ruby.

The other motivation was that I decided I will start Ruby with Test Driven Development so I get into it at the get go.

So, lets get started with a few details about my environment:

  • Ruby 1.9.2
  • Windows 7

Right, lets kick this off by making a prime number calculator. We’ll start with creating both the application and the test files and create the classes.

class PrimeCalc
end

And the testfile

require_relative "../lib/prime_calc.rb"
require "test/unit"

class TestPrimeCalc < Test::Unit::TestCase
end

Nothing too wierd here. In the testfile I require the application file and the test::unit-framework.

So, true to the doctrins of TDD a test is the first starting point.
I want my prime calculator to be able to determine if a number is a prime number or not. An appropriate name would be is_prime? I think.

The test will check to see if 2 is prime.

require_relative "../lib/prime_calc.rb"
require "test/unit"

class TestPrimeCalc < Test::Unit::TestCase
  def test_2_inserted_return_true
    prime_generator = PrimeCalc.new
    actual = prime_generator.is_prime?(2)
    assert_equal(true, actual)
  end
end

And the source code only defines the function (so there’s no runtime error on missing method).

class PrimeCalc
  def is_prime?(num)
  end
end

The test fails and the “Red”-phase in TDD is reached.
See test result below:

F:\temp\> ruby test\test_prime_calc.rb
Loaded suite test/test_prime_calc
Started
F
Finished in 0.003500 seconds.

  1) Failure:
test_2_inserted_return_true(TestPrimeCalc) [test/test_prime_calc.rb:9]:
<true> expected but was
<nil>.

1 tests, 1 assertions, 1 failures, 0 errors, 0 skips

Test run options: --seed 32146

As we can see it expects true to be returned but it gets nil back (if you don’t know why this is; look up return values on methods in Ruby).

So now it’s time for the “green”-phase of TDD. Making the test pass with as little effort as possible. In this case I just let the class return true if 2 is provided, thus.

class PrimeCalc
  def is_prime?(num)
    return true if num == 2
  end
end

And the test passes. Nothing to refactor so let’s go on.

A note is probably in order here: This seems very silly. If 2 is provided then true is returned. Minimum effort to pass the test. Let’s think about this again. Why do more work than needed. If I start doing a whole lot of cases then it’s very easy to introduce bugs or do things that will never be used and just create noise. Alright, onward!

To add some flesh to the actual prime algorith I add a few more test cases. They look exactly the same as the one where 2 was sent in but this time I send in 7.

So, prime theory says that a number is only prime if it can be divided by 1 and itself and no other numbers. From this we can draw the conclusion that any multiple of 2 will never be prime.

First the red-phase. Run the tests and there will be one passing (2) and one failing (7).

Time to fix the failing test. Remember: minimum effort to go to green. In this case I will use the fact that no even numbers can be prime.

class PrimeCalc
  def is_prime?(num)
    return true if num % 2 != 0
  end
end

And run tests again.
One passing and one failing. But, this is wrong. The first test failed, 2 is prime but the method returns false. Not to worry, this one of the benefits of unit testing. Code can be changed and verification is quickly recieved. Here I say that no even numbers can be prime but 2 is even and prime. Let’s change and make sure that 2 returns true.

class PrimeCalc
  def is_prime?(num)
    return true if num == 2
    return true if num % 2 != 0
  end
end

Run unit tests and feel the excitement of all tests passing. However, let’s not rest on our laurels. There’s a lot of cases that aren’t checked. Besides, not all odd numbers are primes. Another test where 13 is used as input. Run tests and have the new test fail. Change the code to pass that test.

Here I do some optimization by not counting higher than the square root of the number itself. Prime theory says that if a factor hasn’t been found before the square root of the number is reached, then no factor will be found (apart from the number itself). Require mathn to get the sqrt method. 2 is already checked so start at 3 and start dividing the number, if a factor is found then the number isn’t prime. If no factor has been found when the loop ends then return true.

class PrimeCalc
  def is_prime?(num)
    return true if num == 2
    return true if num % 2 != 0

    i = 3
    for i in 3..Math.sqrt(num).ceil
      return false if num % i == 0
    end
 
  return true

  end
end

Run tests and see them pass. Nothing to refactor here. Or is there? Let’s take a look at the test code again.

class TestPrimeCalc < Test::Unit::TestCase

  def test_2_inserted_return_true
    prime_generator = PrimeCalc.new
    actual = prime_generator.is_prime?(2)
    assert_equal(true, actual)
  end
  
  def test_7_inserted_return_true
    prime_generator = PrimeCalc.new
    actual = prime_generator.is_prime?(7)
    assert_equal(true, actual)
  end
  
  def test_13_inserted_return_true
    prime_generator = PrimeCalc.new
    actual = prime_generator.is_prime?(13)
    assert_equal(true, actual)
  end

end

See how the line prime_generator = PrimeCalc.new is repeated in all tests? This does not follow the DRY-principle (Don’t repeat yourself). Let’s refactor that using the test frameword method setup and move the offending line in there. Setup is a method that is run before each test.

def setup
    prime_generator = PrimeCalc.new
end

Now we have a prime calculator that can return (with a decent certainty) an answer if a number is prime or not. Are we done? No, not yet. But the post is already long enough so I’ll continue in part 2.

Read Full Post »


The setting
At work I got tasked with continuing maintenance and development of a web site. It uses asp.net, runs on .net framework 3.5 and was a horrible mess. All the code is in one file referenced by codebehind in the aspx-file. About 80% of the labels, grids, buttons and other components are named such things as Label1, GridView3, WebImageButton12.

Setting out
When I started I had the above project dumped in my lap. I had not worked with web development before, there was no handover and the one responsible had moved to London (I work in Sweden).
The first weeks I set out to try to understand the application and the incredible amount of noise, inconsistency and redundancy that it contained.
Time went by, I decided to finally get going with Unit Testing and started with reading The Art of Unit Testing to get the theory and some practical examples. After this I thought I was good to get going on Unit Testing and started a little in my own projects in Ruby. But still, there was this small threshold to get over.
Meanwhile I started reading Working With Legacy Code and after getting through the first two chapters I had broken down what little resistance there was left. Now I would get this monster under test.

So, I downloaded nUnit two days ago and started up. Well, I tried. I spoke to my good friend who has worked with Unit Testing for several years. I ran into some problems and asked him and when I said “It’s a website” he commiserated with me. So, I have to break the functionality out of the codebehind-file and test the buisness logic outside of the website and only use the codebehind to put things on the page. Since I’m in the middle of the release rush it’ll have to wait sadly.

However, when the release is over I’m unit testing this thing.

More to come when I get to it.

Read Full Post »