Junit Solutions Web Applications and Services. Standing on the shoulders of giants l Special thanks...

50
Junit Solutions Web Applications and Services

Transcript of Junit Solutions Web Applications and Services. Standing on the shoulders of giants l Special thanks...

Junit Solutions

Web Applications and Services

Standing on the shoulders of giants

Special thanks to:• Mike Clark

JUnit FAQ Maintainerwww.clarkware.com

• Glenn Vanderburg

Philosophy

“Any program feature without an automated test simply

doesn’t exist.”

from Extreme Programming Explained, Kent Beck

Philosophy

Test-driven development Refactoring Evolution / emergence

Analogy

“It's like trying to build a pyramid by starting at the top, rather than the bottom. No matter how hard you

try, you're doomed to failure.”

from http://www.counterpane.com/comprehensive.html

Testing pyramid

APIDeve

loper time

Delivered application Angry customers

Iteration builds Customer / developer re

work

Assertions

Software must and will change over time. Right? We desire clean uncluttered code. Right? Can we do both? If so, how?

Refactoring

Restructuring of software by applying a series of internal changes

that do not affect its observable behavior

Fowler, Refactoring, 1999

What is JUnit

De facto Java unit testing framework Integrated nicely with IDEs and Ant Easy to learn

Our first unit testpackage org.example.antbook.common;

import junit.framework.TestCase;

public class SearchUtilTest extends TestCase {

public void testSearch() throws Exception { // right API? Document[] docs = SearchUtil.findDocuments("erik");

assertTrue(docs.length > 0); }}

Lean and green

package org.example.antbook.common;

public class SearchUtil {

public static final Document[] findDocuments(String queryString) throws SearchQueryException, SystemException { Document[] results = new Document[1]; return results; }}

Test Runners

Text Swing Ant

Demo

JUnit Assertions

assertTrue(boolean condition)assertFalse(boolean condition)

assertEquals(Object expected, Object actual)• Uses equals() comparison• Overloaded for all primitive types

assertSame(Object expected, Object actual)assertNotSame(Object expected, Object actual)

• Uses == comparison

assertEquals(float expected, float actual, float tolerance)

assertNull(Object o)assertNotNull(Object o)

fail(String message)

+ overloadedString methods

JUnit Design - Pattern dense

Assert

TestCase TestSuite

<<interface>>

Test

YourTest

*

Courtesy of Mike Clarkwww.clarkware.com

JUnit Rules and Conventions

Subclass TestCase Prior to v3.8, String-arg constructor required Test methods

• public void testXXX() [throws …]• Any number of assertions per method

Implement main to run from command-line, but not necessary

Optionally add setUp / tearDown methods.

Test fixturepackage org.example.antbook.ant.lucene;

import java.io.IOException;import junit.framework.TestCase;

public class HtmlDocumentTest extends DocumentTestCase{ HtmlDocument doc;

public void setUp() throws IOException { doc = new HtmlDocument(getFile("test.html")); }

public void testDoc() { assertEquals("Title", "Test Title", doc.getTitle()); assertEquals("Body", "This is some test", doc.getBodyText()); }

public void tearDown() { doc = null; }}

TestCase lifecycle

• setUp• testXXX()• tearDown()• Repeats 1 through 3 for each testXXX

method…

Test Suitespackage org.example.antbook;

import junit.framework.Test;import junit.framework.TestCase;import junit.framework.TestSuite;

import org.example.antbook.junit.SimpleTest;import org.example.antbook.ant.lucene.HtmlDocumentTest;

public class AllTests { static public Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(SimpleTest.class); suite.addTestSuite(HtmlDocumentTest.class); return suite; }}

Demo

JUnit Best Practices

Separate production and test code But typically in the same packages Compile into separate trees, allowing deployment

without tests Don’t forget OO techniques, base classing Test-driven development

– Write failing test first– Write enough code to pass– Refactor– Run tests again– Repeat until software meets goal– Write new code only when test is failing

Base classing

Groups of tests share common needspackage org.example.antbook.ant.lucene;

import java.io.File;import java.io.IOException;import junit.framework.TestCase;

public abstract class DocumentTestCase extends TestCase{ protected File getFile(String filename) throws IOException { String fullname = this.getClass().getResource(filename).getFile();

File file = new File(fullname);

return file; }}

Ant and JUnit

Two great tastes that taste great together You’re building with Ant already, now test with it too! Facilitates continuous integration and testing Richer and more flexible reporting than otherwise

possible, including publishing and e-mailing Fail builds if tests fail

<junit> task

<target name="test-summary" depends="test-compile">

<junit printsummary="yes" haltonfailure="true"> <classpath refid="test.classpath"/> <test name="org….HtmlDocumentTest"/> </junit>

</target>

<batchtest> and formatters<target name="test-batch" depends="test-compile">

<junit printsummary="no" haltonfailure="true"> <classpath refid="test.classpath"/> <formatter type="brief" usefile="false"/> <formatter type="xml"/>

<batchtest todir="${test.data.dir}"> <fileset dir="${test.dir}” includes="**/*Test.class” /> </batchtest>

</junit>

</target>

Tricks of the trade<junit printsummary="no" errorProperty="test.failed" failureProperty="test.failed" fork="${junit.fork}">

<classpath refid="test.classpath"/>

<formatter type="brief" usefile="false"/> <formatter type="xml"/> <test name="${testcase}" todir="${test.data.dir}" if="testcase"/>

<batchtest todir="${test.data.dir}" unless="testcase"> <fileset dir="${test.dir}" includes="**/*Test.class"/> </batchtest>

</junit>

<fail message="Tests failed." if="test.failed"/>

<junitreport>

Aggregates XML generated by <junit> XML formatter output

Transforms result using XSLT into report Default “frames” and “noframes” HTML

stylesheets built-in, but customizable

<junitreport> syntax

<junitreport todir="${test.data.dir}">

<fileset dir="${test.data.dir}"> <include name="TEST-*.xml"/> </fileset>

<report format="frames” todir="${test.reports.dir}" />

</junitreport>

<JUnit Report>

Demo

Ant Best Practices

Use naming conventions:• *Test.java• *TestCase.java

Fork <junit>, typically Use errorproperty and failureproperty Ironically, don’t failonerror Use <fail> after <junit> Use Ant properties for location of XML results and

reports <copy> resources to build directory Pass parameters using <sysproperty>

<copy> test data

<copy todir="${test.dir}"> <fileset dir="test" excludes="**/*.java"/></copy>

Passing parameters to tests<junit printsummary="no”…

<sysproperty key="docs.dir” file="${test.dir}” /> <sysproperty key="index.dir” file="${test.dir}/index” />…</junit>

private String docsDir = System.getProperty("docs.dir");private String indexDir = System.getProperty("index.dir");

Cactus

In-container unit testing Excellent for testing:

• EJB

• Servlets, Filters, Taglibs

• Container-dependent frameworks, like Struts

Cactus Architecture

Cactus test casepackage org.example.antbook;import org.apache.cactus.ServletTestCase;import org.apache.cactus.WebRequest;public class RequestUtilTest extends ServletTestCase{ public void beginGetValueParam(WebRequest theRequest) { theRequest.setURL("localhost:8080", "/antbook", "/test/test.jsp", null, "param=url"); }

public void testGetValueParam() { request.setAttribute("param", "request"); assertEquals("url", RequestUtil.getValue(request, "param")); }

public void testGetValueAttribute() { request.setAttribute("param", "request"); assertEquals("request", RequestUtil.getValue(request, "param")); }}

Back to greenpackage org.example.antbook;

import javax.servlet.http.HttpServletRequest;

public class RequestUtil { public static final String getValue (HttpServletRequest request, String key) { String value = request.getParameter(key); if (value != null) { return value; } value = (String) request.getAttribute(key); if (value != null) { return value; }

return null; }}

Cactus details

<runservertests> task to start, test, stop Deploy

• Tests

• Cactus APIs

• Cactus-enabling web.xml

XDoclet diversion - conditional Cactus<webdoclet destdir="${build.dir}/web/WEB-INF" force="${xdoclet.force}" mergedir="metadata/web"> <fileset dir="src/web"/>

<configParam name="cactusOn” value="${enable.cactus}” /> <deploymentdescriptor validatexml="true" destdir="${build.dir}/${site}" />

</webdoclet>

XDoclet web.xml merge point

<XDtConfig:ifConfigParamEquals paramName="cactusOn” value="true">

<servlet> <servlet-name>ServletRedirector</servlet-name> <servlet-class> org.apache.cactus.server.ServletTestRedirector </servlet-class> </servlet> <servlet> <servlet-name>ServletTestRunner</servlet-name> <servlet-class> org.apache.cactus.server.runner.ServletTestRunner </servlet-class> </servlet>

</XDtConfig:ifConfigParamEquals>

StrutsTestCase

Runs as Mock or on Cactus Provides assertions for expected Struts

ActionErrors and forwards

StrutsTestCase Examplepackage org.example.antbook.struts;

import servletunit.struts.CactusStrutsTestCase;

public class SearchFormTest extends CactusStrutsTestCase { public SearchFormTest(String s) { super(s); }

public void testValidation() { addRequestParameter("query",""); setRequestPathInfo("/search"); actionPerform(); verifyActionErrors(new String[] {"query.required"}); verifyInputForward(); }

}

Cactus - Servlet Test Runner

New addition, allows tests to be run through browser

XML results returned Using XSLT capable browser, report transformed

to HTML on the fly

Other JUnit Extensions

HttpUnit• Parses HTML results into DOM• Easy link navigation and form population• Useful for automated acceptance tests

Canoo WebTest• HttpUnit inside Ant

JUnitPerf• Wrap any JUnit tests• Measure desired performance and scalability tolerances

xUnit

JUnit NUnit CppUnit RubyUnit XMLUnit dbUnit Etc, etc, etc

Continuous Integration

Build often, triggered by commit even Anthill

• Maciej & Urbancode rock!

CruiseControl Gump

Unit testing issues

How do I test database dependent code?• dbUnit

Should I test my user interface? How?• HttpUnit• Canoo WebTest

But, before you test at these levels, see if refactoring is possible

Code Coverage

Clearly see how much is being tested

Conclusions

“Any program feature without an automated test simply doesn’t exist”

Testable code improves confidence and design Easy! “Keep the bar green to keep the code clean!”

Online Resources

JUnit.org• http://www.junit.org

Cactus• http://jakarta.apache.org/cactus

Clover• http://www.thecortex.net/clover

dbUnit• http://www.dbunit.org

HttpUnit• http://www.httpunit.org

Canoo WebTest• http://webtest.canoo.com

Mike Clark’s site• http://www.clarkware.com

Glenn’s reference card• http://www.delphis.com/java/junit-refcard.pdf

Book Resources

eXtreme Programming Explained: Embrace Change & Test Driven Design• Kent Beck

Refactoring: Improving the Design of Existing Code• Martin Fowler (Addison-Wesley, 1999)

Java Tools for Extreme Programming• Rick Hightower and Nick Lesiecki (Wiley, 2001)

Last but not least…

Chapter 4: Testing with JUnit

Chapter 12: coverage of Cactus

Chapter 15: Testing web services

Test-centric throughout

The End

Some examples provided on the symposium CD Examples from my book freely available at http://www.manning.com/antbook

• See Sections/Learning/ch04 specifically Thank You! Q & A

assertTrue(you.willReturnSpeakerEvaluation())