Testing all kinds of Java things using JUnit and its extensions

indexadjustmentInternet και Εφαρμογές Web

13 Νοε 2013 (πριν από 3 χρόνια και 8 μήνες)

77 εμφανίσεις

Testing all kinds of Java things using JUnit
and its extensions

Kalvis Apsitis

What are “Programmer Tests”?


Programmer Testing is the testing
performed by a developer with the goal of
verifying the correct functioning of his/her
own code


Programmer Tests are automated unit
tests that exercise program units such as
classes and methods , not the complete
features or requirements supported by the
software as a whole



Important distinction: if it’s not automated, it’s not
really programmer testing


that’s exploratory
testing.

Safety Net


Having a good test coverage with an
automated unit test harness…


prevents a system from becoming legacy(*),
and


enables effective refactoring, because it also
prevents regression


Metaphor: Mold


Michael Feathers defines “legacy code”
as code without tests


Michael Feathers (2004)


I don't care how good you think your design is. If
I can't walk in and write a test for an arbitrary
method of yours in five minutes its not as good
as you think it is, and whether you know it or not,
you're paying a price for it:


You think your design is good? Pick a class, any
class, and try to instantiate it in a test harness.


Can you make this class work outside the
application?


Can you get it to the point where you can tinker with
it, in real time, build it alone in less than a second,
and add tests to find out what it really does in certain
situations.
Available tools for Java


JUnit (
www.junit.org
)


The de facto Java unit testing framework


Extremely simple, suitable for unit/component testing


Plenty of extensions for J2EE, for example


TestNG (testng.org)


Very close to JUnit but not quite


Can configure test groups/suites in a more flexible
way (it is usable for some integration testing)

JUnit Concepts


Test suite
: Executable row of several Test cases


Test case
: Java class containing test methods


Test fixture
: the initial state of a Test Case, consisting of the
member variables initialized through a method annotated with
@Before. E.g.

@Before

public void setUp() { … }


Test method
: a no
-
argument method of a TestCase class
annotated with @Test (represents a single logical test) For
example:

@Test

public void someMethod() { … }

============

Only things to do in “@Test testA(){...}” methods is
business logic + assertions.

Test fixture contains everything else needed to run the
tests (it is initialized in "@Before setUp(){...}").


TestCase lifecycle

@Before public void setUp() {...}


gets called once before each test method is executed

@After public void tearDown() {...}


gets called once after each test method has been
executed


regardless of whether the test passed or
failed (or threw some other exception).


Each test method gets called exactly once in some order
(test methods should be isolated, so the order does not
matter).


The same instance of a TestCase may or may not be
used for executing the tests


don’t depend on the
constructor, use setUp() for setup!

Suite Example

import org.junit.*;

import org.junit.runner.RunWith;

import org.junit.runners.Suite;


@RunWith(Suite.class)

@Suite.SuiteClasses(value =
{TestCase1.class, TestCase2.class})

public class SomeSuite {


// any code;


// may contain TestCase1, TestCase2


// as inner classes, if their fixtures
have much


// in common

}


Simple Annotations in a
TestCase

import org.junit.*;


public class
TestCase1

{


@Before
protected void setUp()

{super.setUp();}


@After
protected void tearDown()

{super.tearDown();}



@Test
public void methodOne()

{ ... }



@Test(expected = RuntimeException.
class
)


public void testSomeException()

{ ... }



@Ignore(value = "Test method not ready")


@Test


public void ignoredTestCase()

{ ... }

}

Simple TestCase (3)

import org.junit.*;


public class TestCalculator {


private Calculator calculator;


protected void setUp() {


super.setUp();


calculator = new Calculator();


}



@Test


public void
addingPositiveIntegers()

{


int expected = 5;


int actual = calculator.add(2, 3);


assertEquals("2 + 3 should be 5", expected,
actual);


}

}

Test anything that could fail

import org.junit.*;


public class TestCalculator {


private Calculator calculator;


@Before

protected void setUp() { … }


@After


protected void tearDown() { … }



@Test public void addingPositiveIntegers() { … }


@Test public void addingNegativeIntegers() { … }


@Test public void addingZeroes() { … }


@Test public void addingPositiveToNegative() { … }

}

How many method calls are executed?

Test Granularity


Each unit test should check one specific piece of
functionality. Do not combine multiple, unrelated
tests into a single testXXX( ) method.


If the first assertEquals( ) in some method fails,
the remainder is not executed. You won't know if
the other (unrelated) asserts are functional.


One method may contain several asserts, if they
are related, i.e. if the failure of one assert would
cause failures in other asserts anyway.

Exercise 1:

Write a TestCase


Task: Write a JUnit TestCase for the
Template class, place it under

sem02
-
demo/src/main/java/exercise1/TemplateTest.java


There’s a bug in Template class. Try to
find it!


Simple TestSuite

import org.junit.*;

import org.junit.runner.RunWith;

import org.junit.runners.Suite;


@RunWith(Suite.class)

@Suite.SuiteClasses(value =
{CalculatorIntegerTest.class,
CalculatorFloatingPointTest.class,
CalculatorLogarithmsTest.class })

public class Calculator
TestSuite

{


// Can leave empty

}

Exercise 2:

Write a TestSuite


Task:


Write a JUnit TestSuite
exercise2.AllTests

that collects all the TestCase classes from
sem02_demo/src/test/java/exercise2
.

What if I need to do something
expensive in setUp()?


If you need to perform some kind of one
-
time setup
before running the test methods of a TestCase (and
can’t/won’t replace the expensive stuff with a mock
implementation), use @BeforeClass annotation:


public class Example {


@BeforeClass

public static void onlyOnce() { }


@Before

public void beforeEachTest() {}


@Test public void one() { ... }


@Test public void two() { ... }

}

Example of @BeforeClass

public class
InterestCalculatorTestWithTestSetup
{


private static double interestRate, loanAmount;


private static int loanDuration;



public static Test suite() {


TestSuite suite =
new TestSuite(
InterestCalculatorTestWithTestSetup.class
)
;


TestSetup wrapper =
new TestSetup(suite)

{


public void setUp() throws IOException {


ResourceBundle bundle = ResourceBundler.getBundle(


InterestCalculatorTestWithTestSetup.class.getName());


inrerestRate = Double.parseDouble(bundle.getString(“interest.rate”));


loanAmount = Double.parseDouble(bundle.getString(“loan.amount”));


loanDuration = Interest.parseInt(bundle.getString(“loan.duration”));


}


};


return wrapper
;


}



public void testInterestCalculation() { … }

}

Assertions


We already saw one assertion in action, the
assertEquals() method


There are a bunch others


take a look at the
Javadocs for org.junit.Assert


The most often used assertions

assertEquals(),
assertNull(), assertSame(), assertTrue() and
their opposites


are enough for most situations
and the rest you can easily write on your own.


Assert.fail() is used, if control should not reach
that line in the test
-

this makes the test method
to fail.


Testing Expected Exceptions

@Test

public void method1() {


try {


Template t =
new

Template( "My ${f
\
\
oo
template" );


fail("Should NOT allow unterminated variables
in " + t);


}


catch (IllegalArgumentException expected) {}

}


@Test(expected=IllegalArgumentException.class)

public void method2() {


new

Template( "This is my ${f
\
\
oo template" );

}

First approach is longer, but allows more precise

control, where exactly the test method fails.

What happens when an
assertion fails?


The failed assertions create a
org.junit.runner.notification.Failure

when
they fail.


JUnit tags the test as “failure” for later
reference.


If a test throws any other exception (e.g.
NullPointerException), JUnit again catches
these but tags the test as “error” instead of
“failure”.


Mock Objects


At some point, you will face a problem
where the class/method you should be
testing needs to collaborate with an object
that is either difficult to create/obtain or
simply sooo slooow that your test takes
forever to execute (even a 100ms adds up
when you’ve got thousands of tests…).


The solution for this kind of problems is
often to use a mock object.

What are Mock Objects?


A mock object is an object that claims to
implement an interface but doesn’t really.


There are variations on what the “doesn’t
really” part actually means (these
variations are called “fake”, “stub” and
“mock”) but for now, we’ll just call them all
mock objects.



Since there’s nothing like a good
example…

Example: Mock Objects

public class Item {


public float

getPriceAfterDiscounts(
PricingService ps
,
DiscountService ds
)

{


float basePrice =
ps.getPriceFor(
this
)
;


float discountedPrice =
ds.applyDiscounts(this,
basePrice
)
;


return discountedPrice;


}

}


public interface PricingService {


float getPriceFor(
Item item
);

}


public interface DiscountService {


float applyDiscounts(
Item item
,
float basePrice
);

}

How to test that without the real
PricingService & DiscountService?

public class TestItemUsingMockObjects extends TestCase {


private Item item;


private PricingService pricingService;


private DiscountService discountService;



protected void setUp() {


item = new Item();


pricingService = new PricingService() {


public float getPriceFor(Item item) { return 12.34f; }


};


discountService = new DiscountService() {


public float applyDiscounts(Item item, float basePrice) { …

}


};


}



public void testCalculateDiscountsOnBasePrice() {


assertEquals(10.95f, item.getPriceAfterDiscounts(
pricingService
,
discountService
);


}

}

Static vs. Dynamic Mocks


That example used “static” mock objects


the
other school of mock objects is “dynamic” mock
objects


Static mock objects fake their own behavior

while
dynamic mock objects also verify that the
class under test collaborated with the mock
objects as expected


Let’s see an example to illustrate this behavior
-
oriented approach to using mock objects (we’ll
use the EasyMock framework but there are
some alternatives)

Using EasyMock and

dynamic mock objects

public class TestItemUsingMockObjects extends TestCase {


private Item item;


private MockControl pricingControl, discountControl;


private PricingService pricingService;


private DiscountService discountService;



protected void setUp() {


item = new Item();


pricingControl = MockControl.createControl(PricingService.class);
// obtain a “remote control”


pricingService =
(PricingService) pricingControl.getMock();

// let EasyMock create the mock object


pricingService.getPriceFor(item);

// specify expected method call to our mock PricingService


pricingControl.setReturnValue(12.34f);

// fake the behavior by setting return value


pricingControl.replay();

// switch from recording mode to replay mode


// similar setup for DiscountService...


}



public void testCalculateDiscountsOnBasePrice() {


assertEquals(10.95f, item.getPriceAfterDiscounts(
pricingService
, discountService);


pricingControl.verify();
// verify expected interactions actually happened


discountControl.verify();
// verify expected interactions actually happened


}

}

Exercise 1
-
5
-
3:

Using EasyMock


Task:


Complete the test in

session
-
1
-
5/src/exercise3/

TestPricingMachineWithEasyMock.java

using
EasyMock that does the same as
TestPricingMachineWithStaticMockObject.jav
a

does using an anonymous class


The source file includes instructions inline in the
test method to complete

Unit testing database code:

The Problem


The database is a common headache for a
beginning unit tester


Long methods


Complex interfaces (javax.sql)


leading to
verbose setup code even for relatively simple
queries


Need to have the expected data in the database


Slow tests because of the database connection

Unit testing database code:

The Solutions


You can…


Use various mock object libraries
(MockObjects, EasyMock, JMock) to mock the
java.sql.* interfaces and just bite your lip with
the lengthy test classes


Switch to using a better framework for
persistence (e.g. Hibernate)


Refactor your JDBC framework to not rely on
java.sql.* so tightly

Unit testing database code:

What it might look like

public ProjectConfiguration loadProjectConfiguration(String projectId) throws
LocalStorageException {


Query query

= null;


try {


query =
getQueryBuilder().build(
"loadProjectConfiguration"
,



Collections.singletonList(projectId)
);


ResultSet rs

=
getQueryExecuter().executeQuery(query)
;


if (
rs.next()
) {


return
getMapper().makeProjectConfiguration(
rs
);


}


} catch (Exception e) {


e.printStackTrace();


} finally {


if (query != null) {


query.closeResources();


}


}


return null;

}


The dependencies in this piece of code to the java.sql.* interfaces
has been reduced to just one


java.sql.ResultSet. The other, more
complex (to mock) interfaces like java.sql.Connection and
java.sql.PreparedStatement have been hidden inside the
QueryBuilder and Query interfaces.


The next step would be to get rid of java.sql.ResultSet as well in
favor of a more generic tabular data interface which would make
the test code for this class yet another notch cleaner.

Unit testing against a real
database


Regardless of how thin you can squeeze
your persistence framework, it is good
practice to run some tests every now and
then against the real database using the real
JDBC driver, etc. just to make sure that the
thing works end
-
to
-
end.


The problems with this eventually come
down to one big one:


How to populate test data into the database?

DbUnit


JUnit extension for…


Populating test data into the database before
each test execution


Making assertions against the database
contents


Most often uses XML files to read the test
data (referred to as a “data set”) to be
populated but supports also Excel files, a
database
-
driven data set, and enables
relatively simple interfaces for implementing
your own

DbUnit XML files



<?xml version="1.0"?>

<dataset>


<
catalog.coffeebeans

productId
="
000
"
coffeeName
="
Sumatra
"
price
="
750
"/>


<catalog.coffeebeans productId="001" coffeeName="Special Blend"
price="825"/>


<catalog.coffeebeans productId="002" coffeeName="Colombiano"
price="925"/>

</dataset>


Table

Column

Value

Schema

DbUnit test code

public class FindProductsTest
extends

org.dbunit.DatabaseTestCase

{



// called by DbUnit


protected
org.dbunit.database.IDatabaseConnection getConnection()
throws Exception {


Connection connection = …; // open up a connection to the database


return new

DatabaseConnection(
connection
)
;


}




// called by DbUnit


protected
org.dbunit.dataset.IDataSet getDataSet()

throws Exception {


return new

org.dbunit.dataset.xml.FlatXmlDataSet(
new File(“FindProductsTest.xml”)
)
;


}



public void testFindAllProducts() throws Exception {


// test your data access class against the real database,


// populated with test data from FindProductsTest.xml


}

}

Testing J2EE web components


There can be a whole bunch of components
in a J2EE web application that are more or
less dependent on the application server’s
container for correct functioning


Examples include


Java Servlets


JavaServer Pages


Custom Taglibs


Servlet Filters

The problem statement


How to test these components without first
deploying them to the J2EE web container?



Two solutions:


Use a lightweight web container for testing


Mock out the web container



Let’s see how this works out in practice…

Unit testing Java Servlets:

The Servlet being tested

public class AddToShopcartServlet extends HttpServlet {



public void doGet(HttpServletRequest request, HttpServletResponse response)


throws IOException, ServletException {


// obtain the shopping cart (or create one if we don't have one yet)


HttpSession session = request.getSession();


Shopcart shopcart = (Shopcart) session.getAttribute("shoppingCart");


if (shopcart == null) {


shopcart = new Shopcart();


session.setAttribute("shoppingCart", shopcart);


}


// figure out what to do and do it


String itemId = request.getParameter("itemId");


String quantity = request.getParameter("quantity");


shopcart.addItem(itemId, Integer.parseInt(quantity));


// forward to a JSP for rendering the response


RequestDispatcher rd = request.getRequestDispatcher("/showCart.jsp");


rd.forward(request, response);


}

}

Unit testing Java Servlets:

Using the MockObjects lib (1)

public class TestAddToShopcartServlet extends TestCase {


private boolean addItemWasCalled;



public void testAddTwoApples() throws Exception {


MockHttpSession session = new MockHttpSession();


session.setupGetAttribute(“shoppingCart”, new Shopcart() {
=
††††††
pu扬bc=voi搠ad摉瑥t⡓Er楮朠itemf搬dint=qu慮a楴礩={
=
††††††==†
add䥴敭t慳䍡C汥搠=⁴rue;
=
††††††==†
assertEquals(“apple”, itemId);
=

assertEquals(2, quantity);


}


});



...


}

}

Unit testing Java Servlets:

Using the MockObjects lib (2)

public class TestAddToShopcartServlet extends TestCase {


private boolean addItemWasCalled;



public void testAddTwoApples() throws Exception {


MockHttpSession session = ...;



MockHttpServletResponse response = new MockHttpServletResponse();


MockHttpServletRequest request = new MockHttpServletRequest();


request.setupAddParameter(“itemId”, “apple”);
=

request.setupAddParameter(“quantity”, “2”);


request.setSession(session);


request.setExpectedRequestDispatcherURI(“/showCart.jsp”);
=
††††
req略u琮t整異u整e敱略e瑄楳灡瑣桥h⡮敷=jocko敱略e瑄楳灡瑣桥hE⤩;
=
=

...


}

}

Unit testing Java Servlets:

Using the MockObjects lib (3)

public class TestAddToShopcartServlet extends TestCase {


private boolean
addItemWasCalled
;



public void testAddTwoApples() throws Exception {


MockHttpSession session = ...;


MockHttpServletResponse response = ...;


MockHttpServletRequest request = ...;



AddToShopcartServlet servlet = new AddToShopcartServlet();


servlet.init();
// assuming the servlet doesn’t need initialization


servlet.doGet(request, response);



assertTrue(
addItemWasCalled
);


}

}

Exercise 1
-
5
-
4:

Testing Servlets using
MockObjects


Task:


Write a test using the MockObjects library to verify that the
AddToShopcartServlet behaves correctly when adding 2
apples into a shopping cart which already has 5 apples


The file
TestAddToShopcartServletUsingMockObjects.java

already exists in
session
-
1
-
5/src/exercise4/

with inline
comments as exercise instructions.

Exercise 1
-
5
-
5:

Testing Servlets using EasyMock


Task:


Write a test using the EasyMock library to verify that the
AddToShopcartServlet behaves correctly when adding 2
apples into a shopping cart which already has 5 apples


The file
TestAddToShopcartServletUsingEasyMock.java

already exists in
session
-
1
-
5/src/exercise5/

with inline
comments as exercise instructions.

What about Servlet Filters?


Yes, the same technique works equally
well for Servlet Filters
--

You just call
filter.doFilter(request, response, chain)
instead of servlet.doGet(request,
response)…

When using a mock objects
library becomes too much work


Consider using a lightweight web
container such as the
ServletRunner

class that comes with HttpUnit


Pros: may result in cleaner test code
compared to using regular mock objects
libraries when dealing with complex Servlets


Cons: tests take longer to execute because of
all the infrastructure involved (still multiple
magnitudes faster than deploying on a real
container...)

Servlets:

Using ServletRunner (1)

public class TestAddToShopcartServlet extends BaseServletUnitTestCase {



protected void setUp() throws Exception {


Map servlets = new HashMap();


servlets.put(“/AddToShopcart”, AddToShopcartServlet.class.getName());
=
††††
servlets.put(“/showCart.jsp”, FakeServlet.class.getName());
=
††††
pe牶汥ro畮湥爠serv汥l創湮敲===new=perv汥lo畮湥爨
来湥牡瑥g敢塭氨s敲癬整e
F

“/myapp”);
=
†††==
⸮.
=

}



private File generateWebXml(Map servletNamesAndClasses, String contextName) {


// generate a temporary file that acts as the web.xml for ServletRunner's web container


// with <servlet> and <servlet
-
mapping> elements for the servlets listed in the


// 'servletNamesAndClasses' map.


}

}

Servlets:

Using ServletRunner (2)

public class TestAddToShopcartServlet extends BaseServletUnitTestCase {


private InvocationContext addTwoApplesInvocation;



protected void setUp() throws Exception {


ServletRunner servletRunner = …;


...




WebRequest addTwoApplesRequest =


new GetMethodWebRequest(“http://localhost/mmyapp/AddToShopcart”);
=

addTwoApplesRequest.setParameter(“itemId”, “apple”);


addTwoApplesRequest.setParameter(“quantity”, “2”);
=
††††
pe牶汥rr湩瑃l楥湴icli敮琠==
serv汥lo畮湥u
.湥nC汩敮琨F;
=
††††
ad摔d潁灰汥獉湶潣慴楯渠=⁣li敮琮湥n䥮f潣慴楯渨慤摔a潁灰汥獒敱略獴F;
=
††
}
=
}

Servlets:

Using ServletRunner (3)

public class TestAddToShopcartServlet extends BaseServletUnitTestCase {


private InvocationContext addTwoApplesInvocation;



public void testAddToExistingNonEmptyShopcart() throws Exception {


HttpSession session =
addTwoApplesInvocation
.getRequest().getSession();


session.setAttribute(“shoppingCart”, new Shopcart() {
=

{ addItem(“orange”, 5); addItem(“apple”, 4); }


});


new AddToShopcartServlet().service(
addTwoApplesInvocation.
getRequest(),


addTwoApplesInvocation
.getRequest());


...


}

}

Servlets:

Using ServletRunner (4)

public class TestAddToShopcartServlet extends BaseServletUnitTestCase {


private InvocationContext addTwoApplesInvocation;



public void testAddToExistingNonEmptyShopcart() throws Exception {


...


Shopcart shopcart = (Shopcart)
addTwoApplesInvocation
.getRequest()


.getSession().getAttribute(“shoppingCart”);
=
††††
ass敲瑅煵慬eE
“Existing contents of a shopping cart should not be dumped”
I
=
=
††††††==†
5, shopcart.getQuantity(“orange”));
=

assertEquals(
“The quantity should accumulate as items are added”
,


6, shopcart.getQuantity(“apple”));
=
††
}
=
}
=