Translate

Showing posts with label Unit Test. Show all posts
Showing posts with label Unit Test. Show all posts

Monday, July 22, 2013

Creating a JUnit Test Case in Eclipse

Source: https://www.cs.washington.edu/education/courses/143/11wi/eclipse-tutorial/junit.shtml

Using JUnit in Eclipse

JUnit is a Java library to help you perform unit testing. Unit testing is the process of examining a small "unit" of software (usually a single class) to verify that it meets its expectations or specification.
A unit test targets some other "class under test;" for example, the class ArrayIntListTest might be targeting the ArrayIntList as its class under test. A unit test generally consists of various testing methods that each interact with the class under test in some specific way to make sure it works as expected.
JUnit isn't part of the standard Java class libraries, but it does come included with Eclipse. Or if you aren't using Eclipse, JUnit can be downloaded for free from the JUnit web site athttp://junit.org. JUnit is distributed as a "JAR" which is a compressed archive containing Java .class files. Here is a direct link to download the latest JUnit v4.8.2 JAR file.

Creating a JUnit Test Case in Eclipse

To use JUnit you must create a separate .java file in your project that will test one of your existing classes. In the Package Explorer area on the left side of the Eclipse window, right-click the class you want to test and click New → JUnit Test Case.
screenshot
A dialog box will pop up to help you create your test case. Make sure that the option at the top is set to use JUnit 4, not JUnit 3. Click Next.
screenshot
You will see a set of checkboxes to indicate which methods you want to test. Eclipse will help you by creating "stub" test methods that you can fill in. (You can always add more later manually.) Choose the methods to test and click Finish.
screenshot
At this point Eclipse will ask whether you want it to automatically attach the JUnit library to your project. Yes, you do. Select "Perform the following action: Add JUnit 4 library to the build path" and press OK.
screenshot
(If you forget to do add JUnit to your project, you can later add it to your project manually by clicking the top Project menu, then Properties, then Java Build Path, then click Add Library..., and choose JUnit 4 from the list.)
When you're done, you should have a nice new JUnit test case file. I suggest that you change the second import statement at the top to say the following:
import org.junit.*;   // instead of  import org.junit.Test;
screenshot

Writing Tests

Each unit test method in your JUnit test case file should test a particular small aspect of the behavior of the "class under test." For example, an ArrayIntListTest might have one testing method to see whether elements can be added to the list and then retrieved. Another test might check to make sure that the list's size is correct after various manipulations. And so on. Each testing method should be short and should test only one specific aspect of the class under test.
JUnit testing methods utilize assertions, which are statements that check whether a given condition is true or false. If the condition is false, the test method fails. If all assertions' conditions in the test method are true, the test method passes. You use assertions to state things that you expect to always be true, such as assertEquals(3, list.size()); if you expect the array list to contain exactly 3 elements at that point in the code. JUnit provides the following assertion methods:
method name / parametersdescription
assertTrue(test)
assertTrue("message", test) 
Causes this test method to fail if the given boolean test is not true.
assertFalse(test)
assertFalse("message", test) 
Causes this test method to fail if the given boolean test is not false.
assertEquals(expectedValuevalue)
assertEquals("message", expectedValuevalue) 
Causes this test method to fail if the given two values are not equal to each other. (For objects, it uses the equals method to compare them.) The first of the two values is considered to be the result that you expect; the second is the actual result produced by the class under test.
assertNotEquals(value1value2)
assertNotEquals("message", value1value2) 
Causes this test method to fail if the given two values are equal to each other. (For objects, it uses the equals method to compare them.)
assertNull(value)
assertNull("message", value) 
Causes this test method to fail if the given value is not null.
assertNotNull(value)
assertNotNull("message", value) 
Causes this test method to fail if the given value is null.
assertSame(expectedValuevalue)
assertSame("message", expectedValuevalue)
assertNotSame(value1value2)
assertNotSame("message", value1value2) 
Identical to assertEquals and assertNotEquals respectively, except that for objects, it uses the == operator rather than the equals method to compare them. (The difference is that two objects that have the same state might be equals to each other, but not == to each other. An object is only == to itself.)
fail()
fail("message") 
Causes this test method to fail.
Here is a quick example that uses several of these assertion methods.
ArrayIntList list = new ArrayIntList();
list.add(42);
list.add(-3);
list.add(17);
list.add(99);

assertEquals(4, list.size());
assertEquals(17, list.get(2));
assertTrue(list.contains(-3));
assertFalse(list.isEmpty());
Notice that when using comparisons like assertEquals, expected values are written as the left (first) argument, and the actual calls to the list should be written on the right (second argument). This is so that if a test fails, JUnit will give the right error message such as, "expected 4 but found 0".
A well-written test method chooses the various assertion method that is most appropriate for each check. Using the most appropriate assertion method helps JUnit provide better error messages when a test case fails. The previous assertions could have been written in the following way, but it would be poorer style:
// This code uses bad style.
assertTrue(list.size() == 4);         // bad; use assertEquals
assertTrue(list.get(2) == 17);        // bad; use assertEquals
if (!list.contains(-3)) {
    fail();                           // bad; use assertTrue
}
assertTrue(!list.isEmpty());          // bad; use assertFalse and delete the !
Good test methods are short and test only one specific aspect of the class under test. The above example code is in that sense a poor example; one should not test sizeget,contains, and isEmpty all in one method. A better (incomplete) set of tests might be more like the following:
@Test
public void testAddAndGet1() {
    ArrayIntList list = new ArrayIntList();
    list.add(42);
    list.add(-3);
    list.add(17);
    list.add(99);
    assertEquals(42, list.get(0));
    assertEquals(-3, list.get(1));
    assertEquals(17, list.get(2));
    assertEquals(99, list.get(3));

    assertEquals("second attempt", 42, list.get(0));   // make sure I can get them a second time
    assertEquals("second attempt", 99, list.get(3));
}

@Test
public void testSize1() {
    ArrayIntList list = new ArrayIntList();
    assertEquals(0, list.size());
    list.add(42);
    assertEquals(1, list.size());
    list.add(-3);
    assertEquals(2, list.size());
    list.add(17);
    assertEquals(3, list.size());
    list.add(99);
    assertEquals(4, list.size());
    assertEquals("second attempt", 4, list.size());   // make sure I can get it a second time
}

@Test
public void testIsEmpty1() {
    ArrayIntList list = new ArrayIntList();
    assertTrue(list.isEmpty());
    list.add(42);
    assertFalse("should have one element", list.isEmpty());
    list.add(-3);
    assertFalse("should have two elements", list.isEmpty());
}

@Test
public void testIsEmpty2() {
    ArrayIntList list = new ArrayIntList();
    list.add(42);
    list.add(-3);
    assertFalse("should have two elements", list.isEmpty());
    list.remove(1);
    list.remove(0);
    assertTrue("after removing all elements", list.isEmpty());
    list.add(42);
    assertFalse("should have one element", list.isEmpty());
}

...
There is -much- more that could be said about writing effective unit tests, but that is outside the scope of this document. If you are curious, you could learn more by reading pages such as this or this.
You might think that writing unit tests is not useful. After all, we can just look at the code of methods like add or isEmpty to see whether they work. But it's easy to have bugs, and JUnit will catch them better than our own eyes.
Even if we already know that the code works, unit testing can still prove useful. Sometimes we introduce a bug when adding new features or changing existing code; something that used to work is now broken. This is called a regression. If we have JUnit tests over the old code, we can make sure that they still pass and avoid costly regressions.

Running Your Test Case

Once you have written one or two test methods, run your JUnit test case. There are two ways to do this. One way is to click the Run button in the top toolbar (it looks like a green "Play" symbol). A menu will drop down; choose to run the class as a JUnit Test.
screenshot
The other way is to right-click your JUnit test case class and choose Run As → JUnit Test.
screenshot
A new pane will appear showing the test results for each method. You should see a green bar if all of the tests passed, or a red bar if any of the tests failed. If any tests fail, you can view the details about the failure by clicking on the failed test's name/icon and looking at the details in the pane below.
screenshot
Most people think that getting a red failure bar is bad. It's not! It is good; it means that you have found a potential bug to be fixed. Finding and fixing bugs is a good thing. Making a red bar become a green bar (by fixing the code and then re-running the test program) can be very rewarding.

Monday, January 14, 2013

Simply Writing Tests Is Not Test Driven Development

Source: http://spin.atomicobject.com/2012/12/06/writing-tests-is-not-tdd/

Simply Writing Tests Is Not Test Driven Development


There is a common misunderstanding in the software world — simply writing tests is test driven development. Test driven development (TDD) is about ensuring that your software is functioning, as well as ensuring that the software’s internals are well designed, reusable, and decoupled.

What is TDD?

Uncle Bob’s 3 basic rules of TDD are:
  • You are not allowed to write any production code unless it is to make a failing unit test pass.
  • You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
  • You are not allowed to write any more production code than is sufficient to pass the one failing unit test.
To summarize Uncle Bob’s rules:
  • Only write code that is tested.
  • Start your tests small, then work your way up.
  • Only write enough production code to make a test pass.

Basics of TDD

The basic process of TDD has 3 steps: Red, Green, and Refactor.

Red

The red step consists of writing a failing test, only one failing test. If you keep getting ahead of yourself and writing multiple tests, borrow an idea from Getting Things Done and get the ideas out of your head so they don’t get in the way of other thoughts. There are a number of different mechanisms you can use: create a to-do list on paper, make an index card, create a series of TODO comments in your files, etc. I find the physical action of crossing off an item on paper or writing a check mark and folding an index card in half gives me more of a sense of accomplishment.
Your first test for a new object should be simple. TDD focuses on emergent design, the opposite of big design up front. Let the design fall out of your code. The purpose of the first test is not about functionality. It’s about flushing out the usage of what you are about to create.
Start with the inputs: “What do I have to feed into this function?” Next, think about the outputs: “What will this function be spitting out?” Then write an assertion, run your test suite, and verify that the test you just wrote is red.
All the other test cases in the red stage should be about capturing functionality. Don’t just test the happy path; think about the craziest thing you could do with the function or object. What happens when I pass in a null parameter? What happens when I pass in a negative value? How about when I pass in a string when it’s expecting an integer?

Green

The green step consist of making the failing test pass as quickly as possible. If more than one test is failing, start with making the test you just wrote pass, and then continue working the reds to greens one at a time. Don’t worry about how the code looks or how efficient it is. Your concern should be with making the test pass so you can move on to ensuring the next bit of functionality is under test.

Refactor

The next step is refactoring, restructuring, and organizing your code. The refactoring step can occur at anytime — after 1 red/green cycle, after 4 red/green cycles, etc. Since you have a number of passing green tests, you can refactor with ease and comfort, knowing that your tests will fail if you regress and lose functionality.
Refactoring shouldn’t only be about restructuring your code and making it more easily readable. Tests need refactoring love and attention too, but don’t refactor code and tests at the same time.

Benefits of TDD

Working Code

One of the primary benefits of TDD is that you have functioning and working code at all times. You spend time narrowing in on pieces of functionality and ensuring that they work as intended.

Fearless Changes

TDD allows for fearless changes. I worked on a number of software projects prior to being enlightened by the magic of TDD. A common thread of thought, looking back, is that I was always deathly afraid of making changes. I spent more time using the application than I did writing code, just to make sure I was maintaining functionality and not causing regressions in specific features. With TDD, that fear is removed because functionality is under test, and you’re able to get near-instantaneous feedback about the system or parts of it. The ability to make fearless changes via refactoring causes the internal quality of your software to improve and eventually bleeds through to being external quality.

Living Documentation

TDD also provides you with a living documentation of the code. If you are anything like me, then when exploring a new library, you want to skip all the fluffy documentation and cut to the chase, looking at examples on how to use it. It is important that we keep this in mind when writing and refactoring our tests — it’s our responsibility to make the test readable and easily understandable. Unlike comments or extremely long manuals, tests are executable and will tell you if they are lying.

Designing Through Code

I am always troubled that one of the D’s in TDD doesn’t stand for design. As I touched on briefly before, the practice of TDD is not entirely about writing tests, ensuring coverage and working software. TDD flips software development on its head. It forces you to think about the problem from the outside in, instead of from the inside out.
Writing tests first forces you to not be worried or concerned about implementation, the primary worry and concern is with using your object or function. Since we are spending a fair amount of time directly interacting with the objects and functions we are writing, architecture and design come to the forefront.

When Not to TDD

What if you’re using a new library or framework and you don’t know how to use it? How can you write tests first if you don’t know how to begin? The answer is you can’t. Create a new project, and use the new library away from your production code base. This new project is known as a spike. Since this isn’t production code, you aren’t violating Rule #1 of Uncle’s Bob’s 3 basic rules. Code until you feel comfortable with the library. When you know how your library works, ignore the spike, go back to your production code and start writing tests.
However, just because you can’t TDD, do not completely throw away the discipline of writing tests. Your tests will serve as a reference for you and (if your spike is in source control) a reference for those who come behind you. Using these tests, you can quickly recall what you have learned, and you will hopefully be able to look back and see a progression in your technique.