Java tools for XP(Testing your Web App)
Web Apps and Services
Agenda
Introduction Ant Junit
• Inside Junit• How to write Test Case
Testing Webapp Testing Non Functional Req. Conclusion
Introduction
XP VS RUP• Heavy VS Agile
Common Sense• Iterative & incremental• Test emphasis
Is Safe..?
Unit Develop Integration
Test
Safe with Automatic Test
Unit Develop Integration
Test
Junit Cactus
HttpUnit Jmeter JunitPerf
Motivation of Ant
Modern team-based, cross-platform development Developers/users have different environments:
• c:\jdk1.3, d:\jdk1.3, c:\jdk1.2.2 Differences between user and Developers may prefer different IDEs
• IntelliJ, JBuilder, Emacs Users may not have any IDE at all. Frequent releases and distributions.
Ant
Analogous to “make” in Unix For Continuous integration Cross-platform Extensible Open source Defacto standard for Java projects
Ant and J2EE
Why needed in J2EE• Many source, many target• Jar, war, ear• Different Server configuration
Good reference with J2EE • EJB Design Patterns • Java Tools for Extreme Programming
JUnit
Java Unit test library Minimizes delay between bug creation,
detection and correction Proven to increase team productivity Provides regular, concrete feedback to project
managers and sponsors And development team
Makes testing quick and simple Because testable code is better code
Run()
public abstract class TestCase implements Test { private final String fName; public TestCase(String name) { fName= name; } public abstract void run(); … }
Template Method
public void run() { setUp(); runTest(); tearDown(); }
protected void runTest() { } protected void setUp() { } protected void tearDown() { }
TestResult
public void run(TestResult result) { result.startTest(this); setUp(); try { runTest(); } catch (AssertionFailedError e) { //1 result.addFailure(this, e); } catch (Throwable e) { // 2 result.addError(this, e); } finally { tearDown(); } }
TestCase
protected void runTest() throws Throwable { Method runMethod= null; try { runMethod= getClass().getMethod(fName, new Class[0]); } catch (NoSuchMethodException e) { assert("Method \""+fName+"\" not found", false); } try { runMethod.invoke(this, new Class[0]); } }
public class TestSuite implements Test { private Vector fTests= new Vector(); }public void run(TestResult result) { for (Enumeration e= fTests.elements(); e.hasMoreElements(); ) { Test test= (Test)e.nextElement(); test.run(result); } }
Complete Structure
How to Test
Step 1: Write A Test Case Step 2: Write A Test Suite Step 3: Organize The Tests Step 4: Run The Tests
Write A Test Case
Define a subclass of TestCase. Override the setUp() method to initialize object(s) under test. Override the tearDown() method to release object(s) under test. Define one or more public testXXX() methods that exercise the object(s)
under test and assert expected results. Define a static suite() factory method that creates a TestSuite containing
all the testXXX() methods of the TestCase. Optionally define a main() method that runs the TestCase in batch mode.
Define a subclass of TestCase.
public class ShoppingCartTest extends TestCase {
private ShoppingCart _bookCart; private Product _defaultBook;
public ShoppingCartTest(String name) { super(name); }
Setup() & tearDown()
protected void setUp() {
_bookCart = new ShoppingCart(); _defaultBook = new Product("Extreme Programming", 23.95); _bookCart.addItem(_defaultBook);}
protected void tearDown() { _bookCart = null;}
testXXX()
public void testProductAdd() { Product newBook = new Product("Refactoring", 53.95); _bookCart.addItem(newBook); double expectedBalance = _defaultBook.getPrice() + newBook.getPrice(); assertEquals(expectedBalance, _bookCart.getBalance(), 0.0); assertEquals(2, _bookCart.getItemCount());}
public void testEmpty() { _bookCart.empty(); assertTrue(_bookCart.isEmpty());}
Suite()
public static Test suite() { // Reflection is used here to add all Test TestSuite suite = new TestSuite(ShoppingCartTest.class);
// TestSuite suite = new TestSuite(); // suite.addTest(new ShoppingCartTest("testEmpty")); // suite.addTest(new ShoppingCartTest("testProductAdd")); // suite.addTest(new ShoppingCartTest("testProductRemove")); // suite.addTest(new ShoppingCartTest("testProductNotFound")); return suite;}
Main()
public static void main(String args[]) {
junit.textui.TestRunner.run(suite());
}
Write A Test Suite
Define a subclass of TestCase. Define a static suite() factory method that creates a
TestSuite containing all the tests. Optionally define a main() method that runs the TestSuite
in batch mode
Suite()
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTest(ShoppingCartTest.suite());
suite.addTest(CreditCardTestSuite.suite());
return suite;
}
organize
Create test cases in the same package as the code under test.
For each Java package in your application, define a TestSuite class that contains all the tests
Define similar TestSuite classes that create higher-level and lower-level test suites in the other packages of the application.
Sample
AllTests (Top-level Test Suite) SmokeTestSuite (Structural Integrity Tests) EcommerceTestSuite ShoppingCartTestCase CreditCardTestSuite AuthorizationTestCase CaptureTestCase VoidTestCase UtilityTestSuite MoneyTestCase DatabaseTestSuite ConnectionTestCase TransactionTestCase LoadTestSuite (Performance and Scalability Tests) DatabaseTestSuite ConnectionPoolTestCase ThreadPoolTestCase
Our Situation
WEB!!
Approchs to testing webapps
Request-Response• Exercise code by issuing HTTP requests and examining
the responses
In-container• Exercise code from within the container itself
No-container• Exercise code without going through a container
Request-Response
Intuitive and easy to get started Can be hard to maintain without frequent test refactoring, verification of HTML content can be convoluted Applicable to most situations, widely used for acceptance
tests
In Container
Enables low level unit testing, provides access to container specifics e.g. session, config, beans, …
Best for testing complex forward / include logic, code-heavy JSPs, or verifying container implementation differences
No Container
Tests run quickly and in isolation Hard to test JSPs, not indicative of in-container
results
Tools and Frameworks
Request- Response• HttpUnit (sourceforge. net): tests defined in code• JMeter (jakarta): performance and stress testing
In- container• Cactus (jakarta)
No- container• MockObjects (sourceforge. net)
Cactus
Junit is not sufficient with J2EE• How can I get information from Session,
request,response….
Extension of jUnit for J2EE• ServletTestCase• FilterTestCase• JspTestCase
Sequence
Architecture
beginXXX(WebRequest request){}
testXXX(){}
endXXX(WebResponse response){}
setup(){}
tearDown(){}
Sample Test
beginRequestResponse(WebRequest req) throws IOException {
req.addParameter("param1",“World");
}
testRequestResponse() throws IOException {
servlet.doGet(request, response);
}
endRequestResponse(WebResponse res) throws IOException{
DataInputStream dis = new DataInputStream(
res.getConnection().getInputStream());
assertEquals(“Hello World", dis.readLine());
}
Simple Test
public void testXXX(){ SampleServlet servlet = new SampleServlet();
session.setAttribute("name", "value");
String result = servlet.doSomething(request);
assertEquals("something", result); assertEquals("otherValue", session.getAttribute("otherName"));}
HttpUnit
For Functional Test Web Client
• Maintain state• Send request• Retrive response
Methods that simplify verification of response
Sample public void testWelcomePage() throws Exception {
WebConversation conversation = new WebConversation();
WebRequest request = new GetMethodWebRequest( "http://localhost/login.htm" );
WebResponse response = conversation.getResponse( request );
WebForm forms[] = response.getForms();
assertEquals( 1, forms.length );
assertEquals( 3, forms[0].getParameterNames().length );
assertEquals( "id", forms[0].getParameterNames()[2] );
}
Test to Nonfunctional Req.
Functional Test• Is run correctly?
Non Functional Test• Response Time
• Active User
• Transaction per Second
Jmeter
Profiling
JunitPerf
Perfermance Testing Extension Decoraters
• Timed Test• LoadTest• ThreadedTest
Timed Test
public static Test suite() { TestSuite suite = new TestSuite(); long maxTime = 1000 + tolerance; Test testCase = new ExampleTestCase("testOneSecond"); Test timedTest = new TimedTest(testCase, maxTime); suite.addTest(timedTest); return suite;}
Mixed Test
public static Test suite() { int users = 1; int iterations = 10; long maxElapsedTime = 10000 + tolerance; TestSuite suite = new TestSuite(); Test testCase = new ExampleTestCase("testOneSecond"); Test repeatedTest = new RepeatedTest(testCase, iterations); Test loadTest = new LoadTest(repeatedTest, users); Test timedTest = new TimedTest(loadTest, maxElapsedTime); suite.addTest(timedTest); return suite;}
General Rules for Optimization
Don't optimize as you go• making sure that the code is clean, correct, and understandable
Remember the 80/20 rule• use profiling to find out where that 80% of execution time is going
Always run "before" and "after" benchmarks• If slightly faster undo your changes and go back to the original
Use the right algorithms and data structures
Testing Principle
The software does well those things that the tests check.
Test a little, code a little, test a little, code a little...
Make sure all tests always run at 100%. Run all the tests in the system at least once
per day (or night). Write tests for the areas of code with the
highest probability of breakage.
Testing Principle (cont.)
Write tests that have the highest possible return on your testing investment.
If you find yourself debugging using System.out.println(), write a test to automatically check the result instead.
When a bug is reported, write a test to expose the bug. The next time someone asks you for help debugging,
help them write a test. Write unit tests before writing the code and only write
new code when a test is failing.
Conclusion
Junit Bean,EJB
Cactus Servlet JSP
Hole AppHttpUnit
Conclusion (cont.)
Hold App80%
20%
JMeter
JunitPerf
resource
Ant(jakarta.apache.org/ant/) Junit(www.junit.org) Cactus(jakarta.apache.org/cactus) httpUnit(httpunit.sourceforge.net) Jmeter(jakarta.apache.org/jmeter) JunitPerf(www.clarkware.com/software/
JUnitPerf.html)