Junit Basics

Embed Size (px)

Citation preview

  • 7/30/2019 Junit Basics

    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 standardtesting framework for Java development. JUnit, originally created by Kent Beckand Erich Gamma, is an API that enables developers to easily create Java test cases. Itprovides 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 verypattern-dense. Figure 4.1 shows the UML model. The abstract TestCase class is ofmost 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

  • 7/30/2019 Junit Basics

    2/17

    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 evencreate a separate project for the test classes, but we skip this step to make this

    example simpler.

    Create a new source foldertest 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".

  • 7/30/2019 Junit Basics

    3/17

    Alternatively you can add a new source folder by right-clicking on a project and

    selecting NewSource Folder.

    Create a Java class

    In the "src" folder, create the de.vogella.junit.firstpackage and the following

    class.

    de.vogella.junit.first;

    MyClass {

    multiply( x, y) {

    x / y;

    }

    }

    Create a JUnit test

    Right click on your new class in the Package Explorer and select NewJUnit Test

    Case. Select "New JUnit 4 test" and set the source folder to "test", so that your test

    class gets created in this folder.

  • 7/30/2019 Junit Basics

    4/17

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

  • 7/30/2019 Junit Basics

    5/17

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

    Create a test with the following code.

    de.vogella.junit.first;

  • 7/30/2019 Junit Basics

    6/17

    org.junit.Test;

    org.junit.Assert.assertEquals;

    MyClassTest {

    @Test

    testMultiply() {

    MyClass tester = 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 assertis simply a comparison between an expected value andan actual value.

    There are variants of the assert methods for each primitive datatype and forjava.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 theresults, 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 arange, to cope with floating point errors better.There are overloaded versions ofthis method for all Javas primitive types.

    assertEquals(expected, actual) assertEquals(String message, expected, actual)

  • 7/30/2019 Junit Basics

    7/17

    ii. The following method asserts that an object reference equals null.

    assertNull(Object object), assertNull(String message, Object object)

    iii. This asserts that an object reference is not null.

    assertNotNull(Object object), assertNotNull(String message, Object)

    iv. Asserts that the two objects are the same. This is a stricter condition than simpleequality, 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, butJDK 1.4 introduces a new assert keyword. You may encounter source using theolder 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 throughthe code that should not be reached. JUnit uses the term failurefor a test thatfails expectedly, meaning that an assertion was not valid or a fail wasencountered. The term errorrefers to an unexpected error (such as aNullPointerException). We will use the term failuretypically to represent bothconditions as they both carry the same show-stopping weight when encounteredduring a build.

    fail(), fail(String message)

  • 7/30/2019 Junit Basics

    8/17

    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 andcleanup those common objects for each test.

    To create a test fixture, define a setUp() method that initializes common object and atearDown() method to cleanup those objects. The JUnit framework automaticallyinvokes the setUp() method before a each test is run and the tearDown() methodafter 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 timeyourself, but you would quickly grow tired of that. Instead, JUnit provides an object TestSuitewhich runs any number of test cases together. The suite method is like a main method thatis 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;}

  • 7/30/2019 Junit Basics

    9/17

    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 methodsautomatically.

    Note: If you use this way to create a TestSuite all test methods will be added. If you do not wantall 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 thedesired 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. Itassumes the name of the test is the name of the test case method to invoke. It dynamically findsand invokes the test method. The dynamic way is more compact to write but it is less static typesafe.

    An error in the name of the test case goes unnoticed until you run it and get aNoSuchMethodException.

    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. Ithas dropped its strict naming conventions and inheritance hierarchies in favor ofsome exciting new functionality. Here's a quick list of what's new in JUnit 4: Parametric tests Exception tests Timeout tests

  • 7/30/2019 Junit Basics

    10/17

    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 establishedtwo conventions that were essential to its ability to function. The first was that JUnitimplicitly required that any method written to function as a logical test begin with theword test. Any method beginning with that word, such as testUserCreate, wouldbe executed according to a well-defined test process that guaranteed the executionof a fixture both before and after the test method. Second, for JUnit to recognize aclass object containing tests, the class itself was required to extend from JUnit'sTestCase (or some derivation thereof). A test that violated either of these twoconventions 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 testsneed only be decorated with a newly defined @Test annotation.

  • 7/30/2019 Junit Basics

    11/17

    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);}

    @Testpublic 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 tocomprehend.

    1.Testing with annotations

    Java 5 annotations make JUnit 4 a notably different framework from previousversions. In this section, I familiarize you with using annotations in key areas such astest declaration and exception testing, as well as for timeouts and ignoring unwantedor unusable tests.

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

  • 7/30/2019 Junit Basics

    12/17

    Listing 3. Test declaration in JUnit 4import 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;

    @BeforeClasspublic static void setUpBeforeClass() throws Exception {

    pattern = Pattern.compile(zipRegEx);

    }

    @Testpublic 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 asthey 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.

    @Beforepublic void runBeforeEveryTest() {simpleMath = new SimpleMath();}

  • 7/30/2019 Junit Basics

    13/17

    @Afterpublic void runAfterEveryTest() {simpleMath = null;}

    2. @BeforeClass and @AfterClass

    Use @BeforeClass and @AfterClass annotations for class wide setup andtearDown respectively. Think them as one time setup and tearDown. They run forone time before and after all test cases.

    @BeforeClasspublic static void runBeforeClass() {// run for one time before all test cases}

    @AfterClasspublic 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 testthrows Exception. The only time you want to ignore this rule is if you're trying totest for a particular exception. If a test throws an exception, the framework reports afailure.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 typethe 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'vedeclared will result in an IndexOutOfBoundsException:

    Listing 4. Testing for an exception in JUnit 3.8import 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})?$";

  • 7/30/2019 Junit Basics

    14/17

    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 simpletest -- namely writing a try/catch and failing the test if the exception isn't caught.

    Exception testing in JUnit 4The exception test in Listing 5 is no different from the one in Listing 4, except that ituses the new expected parameter. (Note that I was able to retrofit the test from

    Listing 4 by passing in the IndexOutOfBoundsException exception to the @Testannotation.)

    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;

    @BeforeClasspublic 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();

  • 7/30/2019 Junit Basics

    15/17

  • 7/30/2019 Junit Basics

    16/17

    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, asshown in Figure 1:

    Figure 1. How an ignored test shows up in Eclipse

  • 7/30/2019 Junit Basics

    17/17