Showing posts with label unit testing. Show all posts
Showing posts with label unit testing. Show all posts

Tuesday, November 11, 2008

Testing on IE6, IE7 and IE8

Have you ever needed to test your web app in multiple versions if Internet Explorer? It is not quite common to have IE6 and IE7 (or IE8) installed on the same computer side by side.

It is possible. But if you have already upgraded to IE7, there is no going back. Or is there?

Well, you can still test on both browsers. Microsoft quite conveniently produced a free Virtual PC image for IE6 compatibility testing. Go to Internet Explorer Application Compatibility VPC Image page, where you can download images for IE 6, 7 and 8 Beta 2.

  • Internet Explorer 6 on Windows XP SP3
  • Internet Explorer 7 on Windows XP SP2
  • Internet Explorer 8 Beta 2 on Windows XP SP3
  • Internet Explorer 7 on Windows Vista

All you need to run these images is Microsoft Virtual PC. It's all free.

Sunday, November 09, 2008

hCard microformat validator

I was creating a contact page for a website and I wanted to use hCard microformat to encode the address, telephone, fax and the geo location. After I created it, I looked for a way to quickly validate it and I found

hCard microformat Validator 1.0

What a great service! I must tell you, this service is super easy to use. Just type in a URL of the page that contains hCard data and get the results instantly. I read the spec but... I still made few mistakes that were promptly brought to my attention.

I made a typo and instead of adr I used addr, which rendered couple of nested elements invalid. Ok, that was a quick fix.

The telephone number is shop's (this website is for a retail store) and I marked the telephone number as shop, which is invalid. The valid values are: home, work, pref, fax, cell, voice, video, pager, car, msg, modem, bbs, isdn, pcs. Another easy fix! The same went for the fax number.

Lastly, the email address that I entered was of a domain that did not register itself yet. The message I got was:

Lookup of e-mail's domain “notyetregistered.com.au” failedVerify that e-mail address is correct.

Wow, I did not expect such a thorough check!

Another good point for this validator is that it not only tells you what is wrong, but it also links to a FAQ page where the problem is explained.

Well done and thank you! Now this contact page validates 100%!

Sunday, August 26, 2007

Contract of the interfaces

As I mentioned in my previous post titled Don't test everything, unit testing is very valuable in the software development process, but on some occasions the test expects more that it should. In some cases those expectation work fine, in other they fail.

What the hell am I talking about? If you follow the good practice of coding against interfaces, you know that the interface defines the contract all implementation classes must adhere to. If you write an implementation of an interface your unit tests should test possibly everything that this contract specifies and nothing more.

Take a Set from Java Collections Framework for example. This interface is defined as:

A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element. As implied by its name, this interface models the mathematical set abstraction.

At some point I had a unit test that had some input values and my test was assuming the correct results in the set that was returned. I knew that my implementation wa returning a HashSet and I also knew what was in that set. The asserts were quite simple (the code is simplified):

String[] addresses = new String[] {"address1", "address2", "address3"};
Set set = new HashSet(Arrays.asList(addresses));
Iterator i = set.iterator();
assertEquals("address1", i.next());
assertEquals("address2", i.next());
assertEquals("address3", i.next());

It's a good test, you may think. Well, it isn't! This test runs or fails depending on which JDK you use. The problem with this test is that it assumes the order of the elements in the returning set. Set interface does not guarantee the order of its elements. We should not test that.

Let's have a look at the following unit test

import junit.framework.TestCase;

import java.util.Set;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;

public class TestJdkDiff extends TestCase
{
    public void testOrder()
    {
        String javaVersion = System.getProperty("java.version");
        String javaVendor = System.getProperty("java.vendor");
        System.out.println("Running " + javaVendor + " " + javaVersion);

        String[] addresses = new String[] {"address1", "address2", "address3"};
        final Set set = new HashSet(Arrays.asList(addresses));
        for (Iterator i = set.iterator(); i.hasNext();)
        {
            System.out.println(i.next());
        }
    }
}

Execution of the previous test on Java 6 produces the following output

Running Sun Microsystems Inc. 1.6.0_02
address1
address2
address3

The same test executed on Sun's JVM 1.4 prints out

Running Sun Microsystems Inc. 1.4.2_12
address2
address3
address1

Oops! Now I know why my test failed. Even, in JDK the implementation of some classes can change from time to time. As long as the contact is kept all should be fine.

So to fix my initial test one should write

String[] addresses = new String[] {"address1", "address2", "address3"};
Set set = new HashSet(Arrays.asList(addresses));
assertEquals(3, set.size());
assertTrue(set.contains("address1"));
assertTrue(set.contains("address2"));
assertTrue(set.contains("address3"));

Happy coding!

Sunday, August 12, 2007

ArithmeticException vs. NaN

Recently I worked on a small UI widget that renders bars that represent percentage. The percentage could be calculated by a simple formula such as the following:

percentage = value / total * 100%

Imagine a utility method that calculates the percentage and looks like the following:

public static int calcPercentage(int value, int total) {
return 100 * value / total;
}

You could say that this is a fairly simple method and would not be that hard to unit test it. Assuming that the given parameters will never be negative numbers one could whip up a sample test values very quickly.

public void testCalPercentage() {
assertEquals(0, calcPercentage(0, 2));
assertEquals(33, calcPercentage(1, 2));
assertEquals(50, calcPercentage(2, 2));
}

Take a bunch of positive numbers or zeros... Wait! Zeros?! But there could be a possible division by zero! That's right! There is our edge case. Let's test it! Aha! We now get an ArithmeticException.

public void testCalPercentageDivisionByZero() {
try {
assertEquals(0, calcPercentage(0, 0));
fail();
}
catch (ArithmeticException ex) {
// expected
}
}

That's understandable. So we need to work around this and the bar should not be rendered if not values (zeros) were entered.

And here comes the twist! Change the type of spent and remaining variables from primitive int to primitive float.

public static int calcPercentage(float value, float total) {
return (int) (100 * value / total);
}

No exception is thrown anymore. What happened? Why such a different behavior?

It all boils down to how Java and other programming languages handle float point arithmetic. I hope that the following quote sums it all.

Floating-point numbers in the JVM use a radix of two. Floating-point numbers in the JVM, therefore, have the following form:

sign * mantissa * 2 exponent

The mantissa of a floating-point number in the JVM is expressed as a binary number. A normalized mantissa has its binary point (the base-two equivalent of a decimal point) just to the left of the most significant non-zero digit. Because the binary number system has just two digits -- zero and one -- the most significant digit of a normalized mantissa is always a one.

The most significant bit of a float or double is its sign bit. The mantissa occupies the 23 least significant bits of a float and the 52 least significant bits of a double. The exponent, 8 bits in a float and 11 bits in a double, sits between the sign and mantissa. The format of a float is shown below. The sign bit is shown as an "s," the exponent bits are shown as "e," and the mantissa bits are shown as "m":

Bit layout of Java float
s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm

The exponent field is interpreted in one of three ways. An exponent of all ones indicates the floating-point number has one of the special values of plus or minus infinity, or "not a number" (NaN). NaN is the result of certain operations, such as the division of zero by zero. An exponent of all zeros indicates a denormalized floating-point number. Any other exponent indicates a normalized floating-point number.

The JVM throws no exceptions as a result of any floating-point operations. Special values, such as positive and negative infinity or NaN, are returned as the result of suspicious operations such as division by zero. An exponent of all ones indicates a special floating-point value. An exponent of all ones with a mantissa whose bits are all zero indicates an infinity. The sign of the infinity is indicated by the sign bit. An exponent of all ones with any other mantissa is interpreted to mean "not a number" (NaN). The JVM always produces the same mantissa for NaN, which is all zeros except for the most significant mantissa bit that appears in the number. These values are shown for a float below:

Special float values
ValueFloat bits (sign exponent mantissa)
+Infinity0 11111111 00000000000000000000000
-Infinity1 11111111 00000000000000000000000
NaN1 11111111 10000000000000000000000


Things like these we learn when we learn computer programming and tend to forget over time. And then... a simple percentage calculation will remind us.

Happy coding!

Monday, March 19, 2007

Refactoring or Redesign?

If you ever worked on a software that has grown organically over time, you will agree that many candidates for refactoring can be found in it. These are identified as pain points, smells or simply things that suck. The reason is that as the software grows, requirements change and code needs to adapt in order to support these changes.

JIRA is not an exception. JIRA team will spend around 30 hours in each eight week release cycle on refactoring of existing code, improving design, making it easier to extend the current code base with new features. At the iteration planning meeting last week, my colleagues and I were discussing how to spend this time in the most efficient way, debating what is refactoring and what is not. One of my colleagues raised a question: "Is is refactoring or redesign?"

The answer is: "It's both."

According to The Pragmatic Programmer: From Journeyman to Master written by Andrew Hunt and David Thomas:

At its heart, refactoring is redesign. Anything that you or others on your team designed can be redesigned in light of new facts, deeper understandings, changing requirements, and so on. But if you proceed to rip up vast quantities of code with wild abandon, you may find yourself in a worse position that when you started.

Clearly, refactoring is an activity that needs to be undertaken slowly, deliberately, and carefully. Martin Fowler offers the following simple tips on how to refactor without doing more harm than good:

  1. Don't try to refactor and add functionality at the same time.
  2. Make sure you have good tests before you begin refactoring. Run the tests as often as possible. That way you will know quickly if your changes have broken anything.
  3. Take short, deliberate steps: move a field from one class to another, fuse two similar methods into a superclass. Refactoring often involves making many localized changes that result in a larger-scale change. If you keep your steps small, and test after each step, you will avoid prolonged debugging.

Thursday, April 20, 2006

Don't test everything!

There was a lesson we learnt today. Don't test everything if you cannot write the universal test for it.

I recently joined the great team at Atlassian that is behind JIRA - bug tracking, issue tracking and project management and Confluence - the enterprise wiki. We released JIRA version 3.6 just few days ago and we are already working on 3.6.1. The Jira team is more or less pragmatic team and we decided that we will try to do things XP way. We started with story cards on the wall. We took one each, one meaning one pair as we also embrace the pair programming. The first stories we had were mostly outstanding issues or bugs in the current version. We nailed them very well by writing unit or func test to cover or test as much as possible and fixing the bug itself.

Unfortunatelly, there are always some things that are impossible to test. And if not impossible, then so hard to test that the time and effort spent writing such test would not be paid off by the value of the test anyway. So sometimes you can't test, but sometimes you can. The question is how far will you go with your tests. I usually write tests until at least 80% of the existing code is covered by my unit tests. It should be nearly 100% for the new code as you write the test first and then you keep implementing the class until it passes all tests, right? But for the existing code, I am comfortable with anything above 80% (your comfort zone can be different).

In this case we were creating the test around an issue with two sub-tasks and we wanted to make sure that those sub-tasks will be displayed on a particular page in the application. So we wrote the test. The test checked that these sub-tasks appear on the screen one after another. All tests succeeded and we went home happy.

Next morning we learnt a valueable lesson. Our test failed on one of the testing environments (we support and test on JDK 1.3, 1.4, 5.0 + several supported servers and RDBMS on top - helluva lot of tests run everyday, if you can imagine). There was a slight difference in one of the testing environments that resulted in the order of the sub-tasks to be different than in the others. The order of sub-tasks was not important, it was not a business rule, nor a requirement, so we removed the condition that was based on the order of the two. Now the test only verifies that the two sub-tasks are present and the order is ignored. Cool! Everything works now and what we needed is tested.

So the lesson is, stick to the principle of XP and write only minimum code required to implement the required functionality. That applies to your tests as well. Test everything you can, but don't waste time testing something that is not required. Write a test for it once it becomes a requirement.


Creative Commons License This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License.