Post on 24-May-2015
Martijn DashorstTopicus
...and keep your weekends free
Get your applicationin production...
6 WAYS TO KEEP YOUR JOB OUT OF YOUR WEEKEND
1. USE WICKET TESTER
WICKETTESTER
• Test components directly, or their markup
• Runs tests without starting server
• Ajax testing (server side)
• Runs in IDE, ant, maven builds
• Achieves high code coverage
HELLOWORLD TEST
@Test public void labelContainsHelloWorld() { }
HELLOWORLD TEST
@Test public void labelContainsHelloWorld() { WicketTester tester = new WicketTester(); }
HELLOWORLD TEST
@Test public void labelContainsHelloWorld() { WicketTester tester = new WicketTester(); tester.startPage(HelloWorld.class); }
HELLOWORLD TEST
@Test public void labelContainsHelloWorld() { WicketTester tester = new WicketTester(); tester.startPage(HelloWorld.class); tester.assertLabel("message", "Hello, World!"); }
LINK TEST
@Test public void countingLinkClickTest() { }
LINK TEST
@Test public void countingLinkClickTest() { WicketTester tester = new WicketTester(); }
LINK TEST
@Test public void countingLinkClickTest() { WicketTester tester = new WicketTester(); tester.startPage(LinkCounter.class); }
LINK TEST
@Test public void countingLinkClickTest() { WicketTester tester = new WicketTester(); tester.startPage(LinkCounter.class); tester.assertModelValue("label", 0); }
LINK TEST
@Test public void countingLinkClickTest() { WicketTester tester = new WicketTester(); tester.startPage(LinkCounter.class); tester.assertModelValue("label", 0); tester.clickLink("link");}
LINK TEST
@Testpublic void countingLinkClickTest() { WicketTester tester = new WicketTester(); tester.startPage(LinkCounter.class); tester.assertModelValue("label", 0); tester.clickLink("link"); tester.assertModelValue("label", 1);}
NAVIGATION TEST
@Testpublic void navigateToSecondPage() {}
NAVIGATION TEST
@Testpublic void navigateToSecondPage() { WicketTester tester = new WicketTester(); tester.startPage(new FirstPage());}
NAVIGATION TEST
@Testpublic void navigateToSecondPage() { WicketTester tester = new WicketTester(); tester.startPage(new FirstPage()); tester.clickLink("link");}
NAVIGATION TEST
@Testpublic void navigateToSecondPage() { WicketTester tester = new WicketTester(); tester.startPage(new FirstPage()); tester.clickLink("link"); tester.assertRenderedPage(SecondPage.class);}
2. PAGE CHECK
PAGES IN EDUARTE
• Pages must have @PageInfo annotation
• Policy file must contain existing pages
• All secure pages must be in the policy file
PAGE INFO ANNOTATION@PageInfo( title = "Intake stap 1 van 4", menu = {"Deelnemer > intake"})public class IntakePersonalia extends IntakeWizardPage{ ...}
Our build fails for any of these problems...
3. ENTITY CHECKER
Storing Hibernate entities in your pages is bad...
...mkay
WICKET SERIALIZABLE CHECKER
• Runs when page is serialized
• Tries to find non-serializable objects attached to page
• Helpful stacktraces
Unable to serialize class: nl.topicus.project.entities.personen.PersoonField hierarchy is: 2 [class=nl.topicus.project.SomePage, path=2] nl.topicus.project.entities.personen.Persoon nl.topicus.project.SomePage.persoon <----- Entity
EXAMPLE STACKTRACE
WICKET SERIALIZER CHECK
public class TopicusRequestCycle extends WebRequestCycle { public void onEndRequest() { Page requestPage = getRequest().getPage(); testDetachedObjects(requestPage);
if (getRequestTarget() instanceof IPageRequestTarget) { Page responsePage = getRequestTarget().getPage(); if (responsePage != requestPage) { testDetachedObjects(responsePage); } } }}
WICKET SERIALIZER CHECK
if (page == null || page.isErrorPage()) { return;}
try { checker = new EntityAndSerializableChecker( new NotSerializableException()); checker.writeObject(page);} catch (Exception e) { log.error("Couldn't serialize: " + page + ", error : " + ex);}
WICKET SERIALIZER CHECK
private void check(Object obj) { Class cls = obj.getClass(); nameStack.add(simpleName); traceStack.add(new TraceSlot(obj, fieldDescription));
if (!(obj instanceof Serializable) && (!Proxy.isProxyClass(cls))) { throw new WicketNotSerializableException(/* ... */); } ... complicated stuff ...}
WICKET SERIALIZER CHECK
if (!(obj instanceof Serializable) && (!Proxy.isProxyClass(cls))) { throw new WicketNotSerializableException(/* .. */) .toString(), exception);}if (obj instanceof IdObject) { Serializable id = ((IdObject) obj).getIdAsSerializable(); if (id != null && !(id instanceof Long && ((Long) id) <= 0)) { throw new WicketContainsEntityException(/* ... */); }}
... complicated stuff ...
To ensure developers have to fix it immediately...
...an Ajax callback checks for these errors and renders an ErrorPage
4. MARKUP VALIDATOR
VALID MARKUP...
• Nobody cares about valid markup
• XHTML is dead
INVALID MARKUP...
• Browsers do care
• Subtle differences between browser DOM handling
• Ajax becomes a pain
WICKET STUFF HTML VALIDATOR
• http://github.com/dashorst/wicket-stuff-markup-validator
• Based on: http://tuckey.org/validation
ADD DEPENDENCY TO POM
<dependency> <groupId>org.wicketstuff</groupId> <artifactId>htmlvalidator</artifactId> <version>1.0</version> <scope>test</scope></dependency>
ADD FILTER TO WEBAPP
public class MyApplication extends WebApplication { // ...
@Override protected void init() { // only enable the markup filter in DEVELOPMENT mode if(DEVELOPMENT.equals(getConfigurationType())) { getRequestCycleSettings() .addResponseFilter(new HtmlValidationResponseFilter()); } }}
DEFINE PROPER DOCTYPE
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head> <title>Foo</title></head><body></body></html>
... AND ENDURE THE HORRORS OF VALID MARKUP
5. REQUESTLOGGER
REQUEST LOGGER
• HTTPD logs are (mostly) useless for Wicket applications
• POST /vocus/app?wicket:interface=:4:lijst::IBehaviorListener...
• GET /vocus/app?wicket:interface=:1084::
• RequestLogger provides decoded information:
• Page, Listener, RequestTarget, SessionID, etc.
14:00:19 time=101, event=Interface[ target:DefaultMenuLink(menu:personalia:dropitem page: nl.topicus.gui.student.ToonPersonaliaPage(4) interface: ILinkListener:onLinkClicked], response=PageRequest[ nl.topicus.gui.student.ToonLeerlingRelatiesPage(6)] sessioninfo=[ sessionId=D574D35FF49C047E4F290FE clientInfo=ClientProperties{ remoteAddress=192.0.2.50, browserVersionMajor=7, browserInternetExplorer=true}, organization=Demo School username=administrator], sessionstart=Fri Dec 14 13:59:14 CET 2008, requests=14, totaltime=3314
REQUEST LOST PARSER
6. RABID MONITORING
NABAZTAG
• Availability of production applications
• Performance of production applications
• Hudson builds
• Issue tracker
THANK YOU!