Вы находитесь на странице: 1из 17

JUnit Basics

Unit Testing
A unit test is a piece of code written by a developer that executes a specific functionality in the code under test. Unit tests ensure that code is working as intended and validate that this is still the case after code changes.

What is JUnit
JUnit is a member of the xUnit testing framework family and now the de facto standard testing framework for Java development. JUnit, originally created by Kent Beck and Erich Gamma, is an API that enables developers to easily create Java test cases. It provides a comprehensive assertion facility to verify expected versus actual results. For those interested in design patterns, JUnit is also a great case study because it is very pattern-dense. Figure 4.1 shows the UML model. The abstract TestCase class is of most interest to us. JUnit is linked as a JAR at compile-time; the framework resides under packages junit.framework for JUnit 3.8 and earlier and under org.junit for JUnit 4 and later. JUnit features include: Assertions for testing expected results Test fixtures for sharing common test data Test suites for easily organizing and running tests Graphical and textual test runners

Unit Testing with JUnit 4.x JUnit 4.x is a test framework which uses annotations to identify methods that are test methods. JUnit assumes that all test methods can be executed in an arbitrary order. Therefore tests should not depend on other tests. To write a test with JUnit

Annotate a method with @org.junit.Test Use a method provided by JUnit to check the expected result of the code execution versus the actual result

Using JUnit in Eclipse:Create a new project de.vogella.junit.first. We want to create the unit tests in a separate folder. The creation of a separate folder for tests is not mandatory. But it is a good practice to keep the code separated from the regular code. You might even create a separate project for the test classes, but we skip this step to make this example simpler. Create a new source folder test via right-clicking on your project, select "Properties" and choose the "Java Build Path". Select the "Source" tab.

Press "Add folder" then press "Create new folder". Create the folder "test".

Alternatively you can add a new source folder by right-clicking on a project and selecting New Source Folder. Create a Java class In the "src" folder, create the de.vogella.junit.first package and the following class.
package de.vogella.junit.first;

public class MyClass { public int multiply(int x, int y) { return x / y; } }

Create a JUnit test Right click on your new class in the Package Explorer and select New JUnit Test Case. Select "New JUnit 4 test" and set the source folder to "test", so that your test class gets created in this folder.

Press "Next" and select the methods which you want to test.

If the JUnit library in not part of your classpath, Eclipse will prompt you to do so.

Create a test with the following code.


package de.vogella.junit.first;

import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class MyClassTest {

@Test
public void testMultiply() { MyClass tester = new MyClass(); assertEquals("Result", 50, tester.multiply(10, 5)); } }

JUnit Features

1. Asserting desired results


The mechanism by which JUnit determines the success or failure of a test is via assertion statements. An assert is simply a comparison between an expected value and an actual value. There are variants of the assert methods for each primitive datatype and for java.lang.String and java.lang.Object,each with the following signatures: assertEquals(expected, actual) assertEquals(String message, expected, actual)

The second signature for each datatype allows a message to be inserted into the results, which makes clear identification of which assertion failed. i. The following assertion states that the test expected.equals(actual) returns true, or both objects are null. The equality test for a double also lets you specify a range, to cope with floating point errors better.There are overloaded versions of this method for all Javas primitive types. assertEquals(expected, actual) assertEquals(String message, expected, actual)

ii.

The following method asserts that an object reference equals null. assertNull(Object object), assertNull(String message, Object object) This asserts that an object reference is not null. assertNotNull(Object object), assertNotNull(String message, Object)

iii.

iv.

Asserts that the two objects are the same. This is a stricter condition than simple equality, as it compares the object identities using expected == actual. assertSame(Object expected, Object actual), assertSame(String message, Object expected, Object actual)

v.

This assertion fails if the condition is false, printing a message string if supplied. The assertTrue methods were previously named simply assert, but JDK 1.4 introduces a new assert keyword. You may encounter source using the older method names and receive deprecation warnings during compilation. assertTrue(boolean condition), assertTrue(String message, boolean condition)

vi.

The following assertion forces a failure. This is useful to close off paths through the code that should not be reached. JUnit uses the term failure for a test that fails expectedly, meaning that an assertion was not valid or a fail was encountered. The term error refers to an unexpected error (such as a NullPointerException). We will use the term failure typically to represent both conditions as they both carry the same show-stopping weight when encountered during a build. fail(), fail(String message)

2. Using a Test Fixture


A test fixture is useful if you have two or more tests for a common set of objects. Using a test fixture avoids duplicating the test code necessary to initialize and cleanup those common objects for each test. To create a test fixture, define a setUp() method that initializes common object and a tearDown() method to cleanup those objects. The JUnit framework automatically invokes the setUp() method before a each test is run and the tearDown() method after each test is run. Example :The following test uses a test fixture: public class BookTest2 extends TestCase { private Collection collection; protected void setUp() { collection = new ArrayList(); } protected void tearDown() { collection.clear(); } public void testEmptyCollection(){ assertTrue(collection.isEmpty()); } }

3. Using a Test Suite


What is a TestSuite
If you have two tests and you'll run them together you could run the tests one at a time yourself, but you would quickly grow tired of that. Instead, JUnit provides an object TestSuite which runs any number of test cases together. The suite method is like a main method that is specialized to run tests. Create a suite and add each test case you want to execute:

public static void suite(){ TestSuite suite = new TestSuite(); suite.addTest(new BookTest("testEquals")); suite.addTest(new BookTest("testBookAdd")); return suite; }

Since JUnit 2.0 there is an even simpler way to create a test suite, which holds all testXXX() methods. You only pass the class with the tests to a TestSuite and it extracts the test methods automatically. Note: If you use this way to create a TestSuite all test methods will be added. If you do not want all test methods in the TestSuite use the normal way to create it. Example:

public static void suite(){ return new TestSuite(BookTest.class); }

Dynamic and static way of running single tests


JUnit supports two ways (static and dynamic) of running single tests. In static way you override the runTest() method inherited form TestCase class and call the desired test case. A convenient way to do this is with an anonymous inner class. Note: Each test must be given a name, so you can identify it if it fails.

TestCase test = new BookTest("equals test") { public void runTest() { testEquals(); } };


The dynamic way to create a test case to be run uses reflection to implement runTest. It assumes the name of the test is the name of the test case method to invoke. It dynamically finds and invokes the test method. The dynamic way is more compact to write but it is less static type safe. An error in the name of the test case goes unnoticed until you run it and get a NoSuchMethodException.

TestCast test = new BookTest("testEquals");

What's new in JUnit 4 Thanks to Java 5 annotations, JUnit 4 is more lightweight and flexible than ever. It has dropped its strict naming conventions and inheritance hierarchies in favor of some exciting new functionality. Here's a quick list of what's new in JUnit 4: Parametric tests Exception tests Timeout tests

Flexible fixtures An easy way to ignore tests A new way to logically group tests

Out with the old Prior to the addition of Java 5 annotations in JUnit 4, the framework had established two conventions that were essential to its ability to function. The first was that JUnit implicitly required that any method written to function as a logical test begin with the word test. Any method beginning with that word, such as testUserCreate, would be executed according to a well-defined test process that guaranteed the execution of a fixture both before and after the test method. Second, for JUnit to recognize a class object containing tests, the class itself was required to extend from JUnit's TestCase (or some derivation thereof). A test that violated either of these two conventions would not run. Listing 1 shows a JUnit test written prior to JUnit 4. import java.util.regex.Matcher; import java.util.regex.Pattern; import junit.framework.TestCase; public class RegularExpressionTest extends TestCase { private String zipRegEx = "^\\d{5}([\\-]\\d{4})?$"; private Pattern pattern; protected void setUp() throws Exception { this.pattern = Pattern.compile(this.zipRegEx); } public void testZipCode() throws Exception{ Matcher mtcher = this.pattern.matcher("22101"); boolean isValid = mtcher.matches(); assertTrue("Pattern did not validate zip code", isValid); } }

In with the new


JUnit 4 uses Java 5 annotations to completely eliminate both of these conventions. The class hierarchy is no longer required and methods intended to function as tests need only be decorated with a newly defined @Test annotation.

Listing 2 shows the same test seen in Listing 1 but redefined using annotations: Listing 2. Testing with annotations import java.util.regex.Matcher; import java.util.regex.Pattern; import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.assertTrue; public class RegularExpressionTest { private static String zipRegEx = "^\\d{5}([\\-]\\d{4})?$"; private static Pattern pattern; @BeforeClass public static void setUpBeforeClass() throws Exception { pattern = Pattern.compile(zipRegEx); } @Test public void verifyGoodZipCode() throws Exception{ Matcher mtcher = this.pattern.matcher("22101"); boolean isValid = mtcher.matches(); assertTrue("Pattern did not validate zip code", isValid); } } The test in Listing 2 may not be any easier to code, but it certainly is easier to comprehend.

1.Testing with annotations Java 5 annotations make JUnit 4 a notably different framework from previous versions. In this section, I familiarize you with using annotations in key areas such as test declaration and exception testing, as well as for timeouts and ignoring unwanted or unusable tests.

Test declaration
Declaring a test in JUnit 4 is a matter of decorating a test method with the @Test annotation. Note that you need not extend from any specialized class, as Listing 3 shows:

Listing 3. Test declaration in JUnit 4 import java.util.regex.Matcher; import java.util.regex.Pattern; import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.assertFalse; public class RegularExpressionTest { private static String zipRegEx = "^\\d{5}([\\-]\\d{4})?$"; private static Pattern pattern; @BeforeClass public static void setUpBeforeClass() throws Exception { pattern = Pattern.compile(zipRegEx); } @Test public void verifyZipCodeNoMatch() throws Exception{ Matcher mtcher = this.pattern.matcher("2211"); boolean notValid = mtcher.matches(); assertFalse("Pattern did validate zip code", notValid); } }

A note about static imports Java 5's static import feature is used to import the Assert class's assertFalse() method in Listing 3. This is because test classes do not extend from TestCase as they did in previous versions of JUnit. Other Examples:1. @Before and @After Use @Before and @After annotations for setup and tearDown methods respectively. They run before and after every test case. @Before public void runBeforeEveryTest() { simpleMath = new SimpleMath(); }

@After public void runAfterEveryTest() { simpleMath = null; } 2. @BeforeClass and @AfterClass Use @BeforeClass and @AfterClass annotations for class wide setup and tearDown respectively. Think them as one time setup and tearDown. They run for one time before and after all test cases. @BeforeClass public static void runBeforeClass() { // run for one time before all test cases } @AfterClass public static void runAfterClass() { // run for one time after all test cases } 2.Testing for exceptions As with previous versions of JUnit, it's usually a good idea to specify that your test throws Exception. The only time you want to ignore this rule is if you're trying to test for a particular exception. If a test throws an exception, the framework reports a failure.If you'd actually like to test for a particular exception, JUnit 4's @Test annotation supports an expected parameter, which is intended to represent the exception type the test should throw upon execution. A simple comparison demonstrates what a difference the new parameter makes. Exception testing in JUnit 3.8 The JUnit 3.8 test in Listing 4, aptly named testZipCodeGroupException(), verifies that attempting to obtain the third group of the regular expression I've declared will result in an IndexOutOfBoundsException: Listing 4. Testing for an exception in JUnit 3.8 import java.util.regex.Matcher; import java.util.regex.Pattern; import junit.framework.TestCase; public class RegularExpressionTest extends TestCase { private String zipRegEx = "^\\d{5}([\\-]\\d{4})?$";

private Pattern pattern; protected void setUp() throws Exception { this.pattern = Pattern.compile(this.zipRegEx); } public void testZipCodeGroupException() throws Exception{ Matcher mtcher = this.pattern.matcher("22101-5051"); boolean isValid = mtcher.matches(); try{ mtcher.group(2); fail("No exception was thrown"); }catch(IndexOutOfBoundsException e){ } } } This older version of JUnit requires me to do quite a bit of coding for such a simple test -- namely writing a try/catch and failing the test if the exception isn't caught. Exception testing in JUnit 4 The exception test in Listing 5 is no different from the one in Listing 4, except that it uses the new expected parameter. (Note that I was able to retrofit the test from Listing 4 by passing in the IndexOutOfBoundsException exception to the @Test annotation.) Listing 5. Exception testing with the 'expected' parameter import java.util.regex.Matcher; import java.util.regex.Pattern; import org.junit.BeforeClass; import org.junit.Test; public class RegularExpressionJUnit4Test { private static String zipRegEx = "^\\d{5}([\\-]\\d{4})?$"; private static Pattern pattern; @BeforeClass public static void setUpBeforeClass() throws Exception { pattern = Pattern.compile(zipRegEx); } @Test(expected=IndexOutOfBoundsException.class) public void verifyZipCodeGroupException() throws Exception{ Matcher mtcher = this.pattern.matcher("22101-5051"); boolean isValid = mtcher.matches();

mtcher.group(2); } }

3.Testing with timeouts In JUnit 4, a test case can take a timeout value as a parameter. As you can see in Listing 6, the timeout value represents the maximum amount of time the test can take to run: if the time is exceeded, the test fails. Listing 6. Testing with a timeout value @Test(timeout=1) public void verifyFastZipCodeMatch() throws Exception{ Pattern pattern = Pattern.compile("^\\d{5}([\\-]\\d{4})?$"); Matcher mtcher = pattern.matcher("22011"); boolean isValid = mtcher.matches(); assertTrue("Pattern did not validate zip code", isValid); } Testing with timeouts is easy: Simply decorate a method with @Test followed by a timeout value and you've got yourself an automated timeout test! 4.Ignoring tests Prior to JUnit 4, ignoring broken or incomplete tests was a bit of a pain. If you wanted the framework to ignore a particular test, you had to alter its name so as to not follow the test nomenclature. For instance, I often found myself placing a "_" in front of a test method to indicate that the test wasn't made to be run at the current moment. JUnit 4 has introduced an annotation aptly dubbed @Ignore, which forces the framework to ignore a particular test method. You can also pass in a message documenting your decision for unsuspecting developers who happen upon the ignored test. The @Ignore annotation Listing 7 shows how easy it is to ignore a test whose regular expression isn't yet working: Listing 7. Ignore this test @Ignore("this regular expression isn't working yet") @Test public void verifyZipCodeMatch() throws Exception{

Pattern pattern = Pattern.compile("^\\d{5}([\\-]\\d{4})"); Matcher mtcher = pattern.matcher("22011"); boolean isValid = mtcher.matches(); assertTrue("Pattern did not validate zip code", isValid); } Attempting to run this test in Eclipse (for example) will report an ignored test, as shown in Figure 1: Figure 1. How an ignored test shows up in Eclipse

Вам также может понравиться