Wednesday 15 May 2013

java - How should unit tests for beans that perform CRUD operations be designed? -



java - How should unit tests for beans that perform CRUD operations be designed? -

i developing java ee 7 based application uses combination of ejb, cdi , jpa perform create, read, update , delete operations against sql database. develop series of unit tests service layer of application struggling see how can create meaningful unit test cases add together value , aren't unit tests sake of code coverage. of examples i've found in fact integration tests utilize in-memory database.

the service layer of application designed using entity, control, , boundary pattern.

the entity jpa annotated bean containing various getters, setters , named queries, along standard tostring, equals , hashcode methods.

the command cdi managed bean annotated @dependent , contains create, update, delete void methods invoke jpa entity manager persist, merge , remove methods. command contains few read methods utilize either jpa named query or jpa criteria api homecoming list object database. create, update , delete methods perform basic checks such checking whether record exists, 1 time again done via relevant jpa entitymanager methods.

the boundary ejb managed bean annotated @stateless , contains methods recognisable end user such createwidget, deletewidget, updatewidget, activatewidget, discontinuewidget, findallwidgets , findaspecificwidget. more complex entities boundary apply business logic, number of entities simple , don't contain business logic. createwidget, deletewidget, updatewidget, activatewidget, discontinuewidget methods declared void , create utilize of exceptions handle failures such database constraint violation passed web layer of application nowadays user friendly message user.

i know when writing unit tests, should test method in isolation using mocking framework emulate things such entitymanager , when method declared void test case should check whether state of has been changed correctly. issue i'm struggling see how of unit tests doing more checking mocking framework working correctly rather application code.

my question how should design meaningful unit tests validate right operation of boundary , command components, given command component calling various jpa entitymanager methods , boundary component in several cases applying no business logic? alternatively in instance there no benefit , instead should concentrate on writing integration tests.

update

the next illustration of service component used maintain list of widgets:

public class widgetservice { @persistencecontext public entitymanager em; public void createwidget(widget widget) { if (checkifwidgetdiscontinued(widget.getwidgetcode())) { throw new itemdiscontinuedexception(string.format( "widget %s exists , has been discontinued.", widget.getwidgetcode())); } if (checkifwidgetexists(widget.getwidgetcode())) { throw new itemexistsexception(string.format("widget %s exists", widget.getwidgetcode())); } em.persist(widget); em.flush(); } public void updatewidget(widget widget) { em.merge(widget); em.flush(); } public void deletewidget(widget widget) { seek { object ref = em.getreference(widget.class, widget.getwidgetcode()); em.remove(ref); em.flush(); } grab (persistenceexception ex) { throwable rootcause = exceptionutils.getrootcause(ex); if (rootcause instanceof sqlintegrityconstraintviolationexception) { throw new databaseconstraintviolationexception(rootcause); } else { throw ex; } } } public list<widget> findwithnamedquery(string namedqueryname, map<string, object> parameters, int resultlimit) { set<map.entry<string, object>> rawparameters = parameters.entryset(); query query = this.em.createnamedquery(namedqueryname); if (resultlimit > 0) { query.setmaxresults(resultlimit); } (map.entry<string, object> entry : rawparameters) { query.setparameter(entry.getkey(), entry.getvalue()); } homecoming query.getresultlist(); } public list<widget> findwithcomplexquery(int first, int pagesize, string sortfield, sortorder sortorder, map<string, object> filters) { criteriabuilder cb = em.getcriteriabuilder(); criteriaquery<widget> q = cb.createquery(widget.class); root<widget> referencewidget = q.from(widget.class); q.select(referencewidget); //code apply sorting , build filtercondition removed brevity q.where(filtercondition); typedquery<widget> tq = em.createquery(q); if (pagesize >= 0) { tq.setmaxresults(pagesize); } if (first >= 0) { tq.setfirstresult(first); } homecoming tq.getresultlist(); } public long countwithcomplexquery(map<string, object> filters) { criteriabuilder cb = em.getcriteriabuilder(); criteriaquery<long> q = cb.createquery(long.class); root<widget> referencewidget = q.from(widget.class); q.select(cb.count(referencewidget)); //code build filtercondition removed brevity q.where(filtercondition); typedquery<long> tq = em.createquery(q); homecoming tq.getsingleresult(); } private boolean checkifwidgetexists(string widgetcode) { int count; query query = em.createnamedquery(widget.count_by_widget_code); query.setparameter("widgetcode", widgetcode); count = ((number) query.getsingleresult()).intvalue(); if (count == 1) { homecoming true; } else { homecoming false; } } private boolean checkifwidgetdiscontinued(string widgetcode) { int count; query query = em .createnamedquery(widget.count_by_widget_code_and_discontinued); query.setparameter("widgetcode", widgetcode); query.setparameter("discontinued", true); count = ((number) query.getsingleresult()).intvalue(); if (count == 1) { homecoming true; } else { homecoming false; } } }

the next illustration of boundary component used maintain list of widgets:

@stateless public class widgetboundary { @inject private widgetservice widgetservice; public void createwidget(widget widget) { widgetservice.createwidget(widget); } public void updatewidget(widget widget) { widgetservice.updatewidget(widget); } public void deletewidget(widget widget) { widgetservice.deletewidget(widget); } public void activatewidget(string widgetcode) { widget widget; widget = widgetservice.findwithnamedquery(widget.find_by_widget_code, queryparameter.with("widgetcode", widgetcode).parameters(), 0).get(0); widget.setdiscontinued(false); widgetservice.updatewidget(widget); } public void discontinuewidget(widget widget) { widget.setdiscontinued(true); widgetservice.updatewidget(widget); } public list<widget> findwithcomplexquery(int first, int pagesize, string sortfield, sortorder sortorder, map<string, object> filters) { homecoming widgetservice.findwithcomplexquery(first, pagesize, sortfield, sortorder, filters); } public long countwithcomplexquery(map<string, object> filters) { homecoming widgetservice.countwithcomplexquery(filters); } public list<widget> findavailablewidgets() { homecoming widgetservice.findwithnamedquery(widget.find_by_discontinued, queryparameter.with("discontinued", false).parameters(), 0); } }

your code hard test because responsibilities aren't correctly separated.

the widgetboundary doesn't anything, , delegates widgetservice.

the widgetservice mixes business logic (like checking if widget discontinued before creating it) persistence logic (like saving or querying widgets).

that makes widgetboundary dumb, , not worth test, whereas widgetservice complex tested easily.

the business logic should moved boundary (which phone call service). service (which should called dao) should contain persistence logic.

that way, can test queries executed dao work correctly (by populating database test data, calling query method, , see if returns right data).

and can test business logic , quickly, mocking dao. way, don't need database test business logic. example, test of createwidget() method this:

@test(expected = itemdiscontinuedexception) public void createwidgetshouldrejectdiscontinuedwidget() { widgetdao mockdao = mock(widgetdao.class); widgetservice service = new widgetservice(mockdao); when(mockdao.countdiscontinued("somecode").thenreturn(1); widget widget = new widget("somecode"); service.createwidget(widget); }

java unit-testing java-ee mocking

No comments:

Post a Comment