Injustice - Developers Among Us (SciFiDevCon 2024)
Testing Jboss Seam Bottom Up
1. Testing Seam applications from the bottom up. Daniel “Danno” Hinojosa [email_address] http://www.evolutionnext.com Developer, Trainer, Consultant
2. Measuring the Quality of your code? How do you know your software works now? How will you know if your software works tomorrow? How will you know if your software works when...
3. It's the end of an iteration demo time...or even worse, at deployment?
4.
5. @Entity @Table(name = "jsfone_album") public class Album { private Long id; private String name; private List<Artist> artists; .... } @Entity @Table(name = "jsfone_artist") public class Artist { private Long id; private String name; private List<Album> albums; .... } Entities for this small project....Simple Album.java Artist.java
6. @Stateful @Name("findAllAlbumsLikeArtistNameBean") @Scope(ScopeType.CONVERSATION) public class FindAllAlbumsLikeArtistNameBean implements FindAllAlbumsLikeArtistNameLocal, FindAllAlbumsLikeArtistNameRemote { public static final String ejbQL = "SELECT album From Album album left “ + “ join album.artists as artist where “ + “ artist.name like :fuzzyName"; private EntityManager entityManager; private List<Album> result; private String fuzzyName; @In public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } FindAllAlbumsLikeArtistNameBean.java
7. public void setName(String fuzzyName) { this.fuzzyName = fuzzyName; } @SuppressWarnings({"unchecked"}) public void find() { Query query = entityManager.createQuery(ejbQL); query.setParameter("fuzzyName", '%' + fuzzyName + '%'); result = (List<Album>) (List<?>) query.getResultList(); } @Factory("albums") public void initializeAlbums() { result = new ArrayList<Album>(); } @DataModel("albums") public List<Album> getResult() { return result; } @Remove @Destroy public void remove() { } } FindAllAlbumsLikeArtistNameBean.java (Continued)
8.
9. public class MyCalculator { public int add(int i, int j) { return i+j; } } MyCalculator.java public class MyCalculatorTest { @Test public void testNormal() { MyCalculator calc = new MyCalculator(); assertEquals(calc.add(22, 44), 66); } @Test public void testNegative() { MyCalculator calc = new MyCalculator(); assertEquals(calc.add(-2, 2), 0); } } MyCalculatorTest.java
10. public class MyCalculator { public int add(int i, int j) { return i+j; } } MyCalculator.java public class MyCalculatorTest { public void testNormal() { MyCalculator calc = new MyCalculator(); assertEquals(calc.add(22, 44), 66); } public void testNegative() { MyCalculator calc = new MyCalculator(); assertEquals(calc.add(-2, 2), 0); } } MyCalculatorTest.java
12. public class FindAllAlbumsLikeArtistNameBeanUnitTest { @Test public void unitTestFind() { FindAllAlbumsLikeArtistNameBean findAllAlbumsLikeArtistNameBean = new FindAllAlbumsLikeArtistNameBean(); findAllAlbumsLikeArtistNameBean.setName("Prince"); findAllAlbumsLikeArtistNameBean.find(); assertEquals(result.size(), ?); } } FindAllAlbumsLikeArtistNameBeanUnitTest.java First Attempt
14. @Stateful @Name("findAllAlbumsLikeArtistNameBean") @Scope(ScopeType.CONVERSATION) public class FindAllAlbumsLikeArtistNameBean implements FindAllAlbumsLikeArtistNameLocal, FindAllAlbumsLikeArtistNameRemote { public static final String ejbQL = "SELECT album From Album album left “ + “ join album.artists as artist where “ + “ artist.name like :fuzzyName"; private EntityManager entityManager; private List<Album> result; private String fuzzyName; @In public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } FindAllAlbumsLikeArtistNameBean.java
15. public void setName(String fuzzyName) { this.fuzzyName = fuzzyName; } @SuppressWarnings({"unchecked"}) public void find() { Query query = entityManager.createQuery(ejbQL); query.setParameter("fuzzyName", '%' + fuzzyName + '%'); result = (List<Album>) (List<?>) query.getResultList(); } @Factory("albums") public void initializeAlbums() { result = new ArrayList<Album>(); } @DataModel("albums") public List<Album> getResult() { return result; } @Remove @Destroy public void remove() { } } FindAllAlbumsLikeArtistNameBean.java (Continued)
16. How do we test for such dependencies without getting the whole system involved?
18. “ Don't tell your uncle what I said earlier about how he can't get a date because he bathes once a week and scratches himself in public, don't look at his unibrow, and only speak when spoken to.”
20. @Stateful @Name("findAllAlbumsLikeArtistNameBean") @Scope(ScopeType.CONVERSATION) public class FindAllAlbumsLikeArtistNameBean implements FindAllAlbumsLikeArtistNameLocal, FindAllAlbumsLikeArtistNameRemote { public static final String ejbQL = "SELECT album From Album album left “ + “ join album.artists as artist where “ + “ artist.name like :fuzzyName"; private EntityManager entityManager; private List<Album> result; private String fuzzyName; @In public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } FindAllAlbumsLikeArtistNameBean.java For Review:
21. public void setName(String fuzzyName) { this.fuzzyName = fuzzyName; } @SuppressWarnings({"unchecked"}) public void find() { Query query = entityManager.createQuery(ejbQL); query.setParameter("fuzzyName", '%' + fuzzyName + '%'); result = (List<Album>) (List<?>) query.getResultList(); } @Factory("albums") public void initializeAlbums() { result = new ArrayList<Album>(); } @DataModel("albums") public List<Album> getResult() { return result; } @Remove @Destroy public void remove() { } } FindAllAlbumsLikeArtistNameBean.java (Continued) For Review:
22. Mocking the Entity Manager: Creating Mocks for Dependencies List<Album> fakeAlbums = new ArrayList<Album>(); fakeAlbums.add(new Album("Purple Rain")); fakeAlbums.add(new Album("1999")); EntityManager entityManager = createMock(EntityManager.class); Query query = createMock(Query.class); FindAllAlbumsLikeArtistNameBean findAllAlbumsLikeArtistNameBean = new FindAllAlbumsLikeArtistNameBean(); findAllAlbumsLikeArtistNameBean.setEntityManager(entityManager); expect(entityManager.createQuery(FindAllAlbumsLikeArtistNameBean.ejbQL)). andReturn(query); expect(query.setParameter("fuzzyName", "%Prince%")).andReturn(query); expect(query.getResultList()).andReturn(fakeAlbums); replay(entityManager); replay(query); findAllAlbumsLikeArtistNameBean.setName("Prince"); findAllAlbumsLikeArtistNameBean.find(); List<Album> result = findAllAlbumsLikeArtistNameBean.getResult(); assertEquals(result.size(), 2); verify(entityManager); verify(query);
38. @Entity @Table(name = "jsfone_album") public class Album { private Long id; private String name; private List<Artist> artists; .... } @Entity @Table(name = "jsfone_artist") public class Artist { private Long id; private String name; private List<Album> albums; .... } Entities for review Album.java Artist.java
39. <dataset> <jsfone_artist id="1" name="Prince"/> <jsfone_artist id="2" name="Dean Martin"/> <jsfone_artist id="3" name="Black Sabbath"/> <jsfone_album id="1" name="1999"/> <jsfone_album id="2" name="Purple Rain"/> <jsfone_album id="3" name="The Very Best of Prince"/> <jsfone_album id="4" name="Italian Love Songs"/> <jsfone_album id="5" name="The Essential: Dean Martin"/> <jsfone_album id="6" name="Forever Cool"/> <jsfone_album id="7" name="We Sold Our Soul To Rock and Roll"/> <jsfone_album id="8" name="Black Sabbath"/> <jsfone_album id="9" name="Sabotage"/> dbunit/loadartistalbumdata.xml
41. public class FindAllAlbumsLikeArtistNameBeanIntegrationWithDBTest extends DBUnitSeamTest { @Test public void testStuff() throws Exception { new ComponentTest() { @SuppressWarnings({"unchecked"}) protected void testComponents() throws Exception { setValue("#{findAllAlbumsLikeArtistNameBean.name}", "Prince"); invokeMethod("#{findAllAlbumsLikeArtistNameBean.find}"); List<Album> albums = (List<Album>) getValue("#{findAllAlbumsLikeArtistNameBean.result}"); assertEquals(albums.get(0).getArtists().get(0).getName(), "Prince"); assertEquals(albums.size(), 3); } }.run(); } protected void prepareDBUnitOperations() { beforeTestOperations.add( new DataSetOperation("dbunit/loadartistalbumdata.xml") ); } } Cool factor is mucho higher people ! A small app server actually runs with test data! FindAllAlbumsLikeArtistNameBeanIntegrationWithDBTest.java
49. Paying for the real thing can be expensive: @Stateless @Name("creditCardApprover") @Scope(ScopeType.STATELESS) public class CreditCardApproverBean implements CreditCardApproverLocal, CreditCardApproverRemote { @Logger Log log; public boolean approve(String creditCardNumber, int month, int year, int cvv2) { log.debug("Thanks for processing, you have now been charged $.10"); return true; } } CreditCardApproverBean.java
50. @Stateless @Name("creditCardApprover") @Scope(ScopeType.STATELESS) @Install(precedence = MOCK) public class CreditCardApproverMockBean implements CreditCardApproverLocal, CreditCardApproverRemote { public boolean approve(String creditCardNumber, int month, int year, int cvv2) { return creditCardNumber.endsWith("2222"); } } But paying an integration mock, or as I like to call it, an imposter, is cheap. CreditCardApproverMockBean.java
51. @Stateless @Name("processPaymentBean") @Scope(ScopeType.EVENT) public class ProcessPaymentBean implements ProcessPaymentLocal, ProcessPaymentRemote { private CreditCardApprover creditCardApprover; private String creditCardNumber; private int year; private int month; private int cvv2; @In(value = "creditCardApprover", create = true) public void setCreditCardApprover(CreditCardApprover creditCardApprover) { this.creditCardApprover = creditCardApprover; } //Getters and setters for year, month, cvv2 assumed public boolean process() { return creditCardApprover.approve(creditCardNumber, month, year, cvv2); } } Assuming we have interface driven design....Mocks win in integration ProcessPaymentBean.java
52. A review of what some great open source products you should know about.
53.
54.
55.
56. I'm kind of tired now, I think I'm gonna go home..... .... but Mama always says leave time for Q&A. She says it's the right thing to do.