From: Verma, N. (G. B. L529706) <Nit...@ge...> - 2002-03-07 21:14:47
|
Hi Brian, MockObject: are not just to emulating behavior of an object but also to make us de-couple tests form the real systems! " My intent is to assert my logic not assert *is there is a network connection* or *is the database up*" When I need to assert some logic on the data I receive from the database I don't use mockobjects, as I need the real system! Any comments... Reagrds, Nitin Verma -----Original Message----- From: Brian Denny [mailto:br...@dl...] Sent: Friday, March 08, 2002 2:29 AM To: moc...@li... Subject: [Mockobjects-java-users] simulating vs. reproducing behavior hi, i'm fairly new to Mock Objects, and trying to wrap my head around where to draw the line between simulating and reproducing behavior. I came across some code in our system recently which, under certain conditions, calls deleteRow() on a ResultSet and then immediately calls updateRow() on the same ResultSet. I wasn't sure off the top of my head whether that was "legal", whether it should throw an exception, or what the expected behavior was according to the JDBC spec. I was in the middle of trying to hack up a mock implementation for updatable result sets, so it seemed particularly relevant. My first instinct is that, whatever correct behavior is, the MockResultSet should mimic it. So if, for example, "rs.deleteRow(); rs.updateRow();" should throw an exception, then my MockResultSet should throw an exception. But the more details of that nature you try to simulate, the closer you get to really reproducing the functionality instead of just emulating it. To *really* simulate correct behavior, then after you update a ResultSet you should be able to iterate back over it and see the updates you just made reflected in your get() calls. But by that point, you have practically created a real implementation of a ResultSet! Where do you draw the line? In a related vein, it seems to me that limiting yourself to emulating the functionality in as minimal a way as possible, does tend to make your test code rely on implementation details. For example, we have some code which provides an abstraction over the process of inserting/updating/deleting several rows of a table based on the states of objects which "know" how to read and write themselves to/from the table. Now, this code might work by using an updatable result set, or it might work by calling executeUpdate methods on a statement. Using mock objects, I have to test things like the particular query string used, or the number of execute calls, or (if it uses an updatable result set) the number of updateRow(), insertRow(), or deleteRow() calls. All of these tests will break if I change the internal implementation details of the code I am trying to test. It seems that to test the abstraction instead of the implementation, you have to do a more thorough job of emulating the behavior than the Mock Objects pattern really advocates. Does anyone have comments or advice about this? To make things concrete: let's say I'm implementing a mock updatable result set. Would you code it so that the update methods actually alter the table rows? Or would you just provide for expectations of how many times updateRow(), etc. get called? -brian _______________________________________________ Mockobjects-java-users mailing list Moc...@li... https://lists.sourceforge.net/lists/listinfo/mockobjects-java-users "THIS E-MAIL MESSAGE ALONG WITH ANY ATTACHMENTS IS INTENDED ONLY FOR THE ADDRESSEE and may contain confidential and privileged information. If the reader of this message is not the intended recipient, you are notified that any dissemination, distribution or copy of this communication is strictly Prohibited. If you have received this message by error, please notify us immediately, return the original mail to the sender and delete the message from your system." |
From: Verma, N. (G. B. L529706) <Nit...@ge...> - 2002-03-07 23:26:36
|
Just leave mockobject for sec! Can you access PreparedStatement/Statement of your SQLObject object? if not ... you can't rollback a transaction properly. You will end-up having open cursors! (assert on to the closing of Statements) I can smell duplication of code here! ------------ if (obj.isMarkedDeleted()) { // jdbc code to delete obj from table } else if (obj.isMarkedChanged()) { // jdbc code to update table row for obj } else if (obj.isMarkedNew()) { // jdbck code to insert obj into table } else { // do nothing } ----------- What I fell is you are making the query string in this util class. I suggest you to make something like this: public interface SQLObject{ public void execute() throws SQLException; public void close() throws SQLException; } public class SQLObjectFactory{ public static SQLObject getSQLObject(final String operation){ // use an XML/properties file to get information on SQLObjects // Like "insert=blah.blah.InsertSQLObject" SQLObject objSQLObject = SQLConfig.getSQLObject(operation); if(objSQLObject==null){ return new NullSQLObject(); } return objSQLObject; } } public class NullSQLObject implements SQLObject{ public void execute(){ //do nothing } public void close(){ //do nothing } } public abstract AbstarctSQLObject implements SQLObject{ public final void execute throws SQLException{ //excute code using getQuery() } public final void close(){ //Code to close } protected abstract Striing getQuery(); } public class DeleteSQLObject{ protected String getQuery() // code } } public class InsertSQLObject{ protected String getQuery() // code } } public class UpdateSQLObject{ protected String getQuery() // code } } public class SQLObjectComposite{ public void addSQLObject(SQLObject objSQLObject){ //... } puclic SQLObjectIterator iterator(){ //... } public void closeAll()throws SQLException{ //... } } ----- public void update(Connection conn, SQLObjectComposite objSQLObjectComposite) { SQLObjectIterator iterator = objSQLObjectComposite.iterator(); (iterator.hasNext()) { try{ iterator.next().excute(); conn.commit(); } catch(){ conn.rollback(); } finally{ objSQLObjectComposite.closeAll(); conn.close(); } } } ------ Now .. back to mack stuff: Now u can assert for DeleteSQLObject/InsertSQLObject/UpdateSQLObject one by one and assert for commit/rollback/close in your UpdateTest! Comments? Regards, Nitin Verma -----Original Message----- From: Brian Denny [mailto:br...@dl...] Sent: Friday, March 08, 2002 3:52 AM To: Verma, Nitin (GEA, BLR, L529706) Cc: moc...@li... Subject: Re: [Mockobjects-java-users] simulating vs. reproducing behavior perhaps i should give a more concrete example. i have this utility method to stick a bunch of objects in a database. each object corresponds to a table row and "knows" how to read/write itself into a table. // pseudojava public void update(Connection conn, SQLObject[] objs) { for (each obj in objs) { if (obj.isMarkedDeleted()) { // jdbc code to delete obj from table } else if (obj.isMarkedChanged()) { // jdbc code to update table row for obj } else if (obj.isMarkedNew()) { // jdbck code to insert obj into table } else { // do nothing } } } now i want to test this method. so i write something like: public void testUpdate throws Exception { // object declarations and creation snipped obj1.markNew(); obj2.markChanged(); obj3.markDeleted(); mockStatement.setExpectedExecuteCalls(3); dbUtility.update(mockConnection, new SQLObject[] { obj1, obj2, obj3 }); mockStatement.verify(); } hmm. the above doesn't actually test that i did one insert, one delete, and one update. so i could do the three updates separately -- one for each object -- and test the query string each time: public void testUpdate throws Exception { // object declarations and creation snipped obj1.markNew(); obj2.markChanged(); obj3.markDeleted(); mockStatement.setExpectedExecuteCalls(1); mockStatement.setExpectedQueryString( "INSERT INTO objecttable values ('this is painful')"); dbUtility.update(mockConnection, new SQLObject[] { obj1 }); mockStatement.verify(); mockStatement.setExpectedExecuteCalls(1); mockStatement.setExpectedQueryString("UPDATE objecttable set ..."); dbUtility.update(mockConnection, new SQLObject[] { obj2 }); mockStatement.verify(); ... etc. ... } this is better, although now i'm not testing my ability to do all three at once. but now i decide to change my implementation of update() so that instead of calling executeUpdate() on a Statement, it gets a ResultSet, and goes through it updating and deleting and inserting rows as appropriate. oops. my tests just broke. i'm not sure what to think about this. since there is always more than one way to do an update in JDBC, it seems to follow that there is no implementation-INdependent way to test a method which is supposed to perform some set of JDBC updates depending on the state of something-or-other, if the only tools you have are tools that test which particular JDBC methods or SQL queries you performed. i don't see the point of writing implementation-dependent tests. i want to test the input-output behavior of my methods, not how they function. comments? -brian "THIS E-MAIL MESSAGE ALONG WITH ANY ATTACHMENTS IS INTENDED ONLY FOR THE ADDRESSEE and may contain confidential and privileged information. If the reader of this message is not the intended recipient, you are notified that any dissemination, distribution or copy of this communication is strictly Prohibited. If you have received this message by error, please notify us immediately, return the original mail to the sender and delete the message from your system." |
From: Steve F. <st...@m3...> - 2002-03-11 00:27:50
|
From: "Verma, Nitin (GEA, BLR, L529706)" <Nit...@ge...> > Just leave mockobject for sec! > [...] I was going to respond this weekend, but you said everything I was going to. Nice job. Steve |
From: Verma, N. (G. B. L529706) <Nit...@ge...> - 2002-03-07 23:34:44
|
DeleteSQLObject/InsertSQLObject/UpdateSQLObject extend...AbstarctSQLObject sorry I missed -----Original Message----- From: Verma, Nitin (GEA, BLR, L529706) [mailto:Nit...@ge...] Sent: Friday, March 08, 2002 4:57 AM To: Brian Denny Cc: moc...@li... Subject: RE: [Mockobjects-java-users] simulating vs. reproducing behavior Just leave mockobject for sec! Can you access PreparedStatement/Statement of your SQLObject object? if not ... you can't rollback a transaction properly. You will end-up having open cursors! (assert on to the closing of Statements) I can smell duplication of code here! ------------ if (obj.isMarkedDeleted()) { // jdbc code to delete obj from table } else if (obj.isMarkedChanged()) { // jdbc code to update table row for obj } else if (obj.isMarkedNew()) { // jdbck code to insert obj into table } else { // do nothing } ----------- What I fell is you are making the query string in this util class. I suggest you to make something like this: public interface SQLObject{ public void execute() throws SQLException; public void close() throws SQLException; } public class SQLObjectFactory{ public static SQLObject getSQLObject(final String operation){ // use an XML/properties file to get information on SQLObjects // Like "insert=blah.blah.InsertSQLObject" SQLObject objSQLObject = SQLConfig.getSQLObject(operation); if(objSQLObject==null){ return new NullSQLObject(); } return objSQLObject; } } public class NullSQLObject implements SQLObject{ public void execute(){ //do nothing } public void close(){ //do nothing } } public abstract AbstarctSQLObject implements SQLObject{ public final void execute throws SQLException{ //excute code using getQuery() } public final void close(){ //Code to close } protected abstract Striing getQuery(); } public class DeleteSQLObject{ protected String getQuery() // code } } public class InsertSQLObject{ protected String getQuery() // code } } public class UpdateSQLObject{ protected String getQuery() // code } } public class SQLObjectComposite{ public void addSQLObject(SQLObject objSQLObject){ //... } puclic SQLObjectIterator iterator(){ //... } public void closeAll()throws SQLException{ //... } } ----- public void update(Connection conn, SQLObjectComposite objSQLObjectComposite) { SQLObjectIterator iterator = objSQLObjectComposite.iterator(); (iterator.hasNext()) { try{ iterator.next().excute(); conn.commit(); } catch(){ conn.rollback(); } finally{ objSQLObjectComposite.closeAll(); conn.close(); } } } ------ Now .. back to mack stuff: Now u can assert for DeleteSQLObject/InsertSQLObject/UpdateSQLObject one by one and assert for commit/rollback/close in your UpdateTest! Comments? Regards, Nitin Verma -----Original Message----- From: Brian Denny [mailto:br...@dl...] Sent: Friday, March 08, 2002 3:52 AM To: Verma, Nitin (GEA, BLR, L529706) Cc: moc...@li... Subject: Re: [Mockobjects-java-users] simulating vs. reproducing behavior perhaps i should give a more concrete example. i have this utility method to stick a bunch of objects in a database. each object corresponds to a table row and "knows" how to read/write itself into a table. // pseudojava public void update(Connection conn, SQLObject[] objs) { for (each obj in objs) { if (obj.isMarkedDeleted()) { // jdbc code to delete obj from table } else if (obj.isMarkedChanged()) { // jdbc code to update table row for obj } else if (obj.isMarkedNew()) { // jdbck code to insert obj into table } else { // do nothing } } } now i want to test this method. so i write something like: public void testUpdate throws Exception { // object declarations and creation snipped obj1.markNew(); obj2.markChanged(); obj3.markDeleted(); mockStatement.setExpectedExecuteCalls(3); dbUtility.update(mockConnection, new SQLObject[] { obj1, obj2, obj3 }); mockStatement.verify(); } hmm. the above doesn't actually test that i did one insert, one delete, and one update. so i could do the three updates separately -- one for each object -- and test the query string each time: public void testUpdate throws Exception { // object declarations and creation snipped obj1.markNew(); obj2.markChanged(); obj3.markDeleted(); mockStatement.setExpectedExecuteCalls(1); mockStatement.setExpectedQueryString( "INSERT INTO objecttable values ('this is painful')"); dbUtility.update(mockConnection, new SQLObject[] { obj1 }); mockStatement.verify(); mockStatement.setExpectedExecuteCalls(1); mockStatement.setExpectedQueryString("UPDATE objecttable set ..."); dbUtility.update(mockConnection, new SQLObject[] { obj2 }); mockStatement.verify(); ... etc. ... } this is better, although now i'm not testing my ability to do all three at once. but now i decide to change my implementation of update() so that instead of calling executeUpdate() on a Statement, it gets a ResultSet, and goes through it updating and deleting and inserting rows as appropriate. oops. my tests just broke. i'm not sure what to think about this. since there is always more than one way to do an update in JDBC, it seems to follow that there is no implementation-INdependent way to test a method which is supposed to perform some set of JDBC updates depending on the state of something-or-other, if the only tools you have are tools that test which particular JDBC methods or SQL queries you performed. i don't see the point of writing implementation-dependent tests. i want to test the input-output behavior of my methods, not how they function. comments? -brian "THIS E-MAIL MESSAGE ALONG WITH ANY ATTACHMENTS IS INTENDED ONLY FOR THE ADDRESSEE and may contain confidential and privileged information. If the reader of this message is not the intended recipient, you are notified that any dissemination, distribution or copy of this communication is strictly Prohibited. If you have received this message by error, please notify us immediately, return the original mail to the sender and delete the message from your system." _______________________________________________ Mockobjects-java-users mailing list Moc...@li... https://lists.sourceforge.net/lists/listinfo/mockobjects-java-users "THIS E-MAIL MESSAGE ALONG WITH ANY ATTACHMENTS IS INTENDED ONLY FOR THE ADDRESSEE and may contain confidential and privileged information. If the reader of this message is not the intended recipient, you are notified that any dissemination, distribution or copy of this communication is strictly Prohibited. If you have received this message by error, please notify us immediately, return the original mail to the sender and delete the message from your system." |
From: Verma, N. (G. B. L529706) <Nit...@ge...> - 2002-03-08 00:18:54
|
Yups! -----Original Message----- From: Brian Denny [mailto:br...@dl...] Sent: Friday, March 08, 2002 5:40 AM To: Verma, Nitin (GEA, BLR, L529706) Subject: Re: [Mockobjects-java-users] simulating vs. reproducing behavior so, all your updates will be done using an SQL query, rather than by using an updatable result set, right? -brian > public abstract AbstarctSQLObject implements SQLObject{ > > > public final void execute throws SQLException{ > > //excute code using getQuery() > } > > public final void close(){ > //Code to close > } > > protected abstract Striing getQuery(); > > } "THIS E-MAIL MESSAGE ALONG WITH ANY ATTACHMENTS IS INTENDED ONLY FOR THE ADDRESSEE and may contain confidential and privileged information. If the reader of this message is not the intended recipient, you are notified that any dissemination, distribution or copy of this communication is strictly Prohibited. If you have received this message by error, please notify us immediately, return the original mail to the sender and delete the message from your system." |
From: Brian D. <br...@dl...> - 2002-03-15 00:52:15
|
i think that our previous conversation diverged a bit from the point i was trying to make. here are three possible versions of a test method: // VERSION A // public void testUpdate() throws Exception { myMockStatement.setExpectedQueryString(UPDATE_QUERY); myMockStatement.setExpectedExecuteCalls(1); mySQLObj.setMyInstanceVariable("different value"); // marks isChanged SQLUtils.update(myMockConnection, objs); myMockStatement.verify(); } // VERSION B // public void testUpdate() throws Exception { myMockResultSet.setupRows( { ... } ); myMockResultSet.setExpectedUpdateRowCalls(1); // this line tests implementation details mySQLObj.setMyInstanceVariable("different value"); // marks isChanged SQLUtils.update(myMockConnection, objs); myMockResultSet.verify(); } // VERSION C // public void testUpdate() throws Exception { myMockResultSet.setupRows( { ... } ); SQLObject[] objs = SQLUtils.fetch(myMockConnection, IDS_TO_FETCH); objs[0].setMyInstanceVariable("different value"); // marks isChanged SQLUtils.update(myMockConnection, objs); SQLObject[] objs = SQLUtils.fetch(myMockConnection, IDS_TO_FETCH); assertEquals("different value", objs[0].getMyInstanceVariable()); } Version A seems very MockObjects-ish. i think it is clear that Version A assumes that you are performing the update by calling executeUpdate() on a statement. Version B also seems very MockObjects-ish. version B, on the other hand, assumes that you are performing the update by getting a resultset and calling rs.updateRow() on the resultset. Version C doesn't seem very MockObjects-ish, primarily because you'd have to implement a lot of not-so-mock functionality in your mock objects to make it work. but Version C is the only one of the three that really specifies what i am trying to test. if i am writing a function to update a bunch of objects in a database, what i care about is that the objects got updated correctly. i don't care HOW they got updated. Version A and B both test that the update happened a certain way. maybe this whole dilemma is the fault of JDBC, because it provides 2 different *interfaces* for accomplishing the same functionality...? but on the other hand, it would be nice to be able to write test code that would pass even if i decided to re-implement my update() by making a raw socket connection to the SQL server... -brian > Yups! > > > > > > so, all your updates will be done using an SQL query, rather than by using > > an updatable result set, right? > > > -brian > |
From: Steve F. <st...@m3...> - 2002-03-15 23:44:10
|
From: "Brian Denny" <br...@dl...> > but Version C is the only one of the three that really specifies what i am > trying to test. if i am writing a function to update a bunch of objects in > a database, what i care about is that the objects got updated correctly. i > don't care HOW they got updated. Version A and B both test that the update > happened a certain way. > > maybe this whole dilemma is the fault of JDBC, because it provides 2 > different *interfaces* for accomplishing the same functionality...? but on > the other hand, it would be nice to be able to write test code that would > pass even if i decided to re-implement my update() by making a raw socket > connection to the SQL server... it seems to me that, at some point, you have to exercise your jdbc code. When you come to do that, I hope you find mocks useful. If version C is what you want to achieve, then maybe you need some intermediate objects to buffer that which, in turn, can be mocked up? Steve |
From: Verma, N. (G. B. L529706) <Nit...@ge...> - 2002-03-11 10:17:20
|
Hi Steve, Thanx! I suppose anyone havin' bit of XP in blood will have similar answer! Anyway special thanx to my guru "Chad Fowler"! Cheers XP! Regards, Nitin Verma -----Original Message----- From: Steve Freeman [mailto:st...@m3...] Sent: Monday, March 11, 2002 5:22 AM To: moc...@li... Subject: Re: [Mockobjects-java-users] simulating vs. reproducing behavior From: "Verma, Nitin (GEA, BLR, L529706)" <Nit...@ge...> > Just leave mockobject for sec! > [...] I was going to respond this weekend, but you said everything I was going to. Nice job. Steve _______________________________________________ Mockobjects-java-users mailing list Moc...@li... https://lists.sourceforge.net/lists/listinfo/mockobjects-java-users "THIS E-MAIL MESSAGE ALONG WITH ANY ATTACHMENTS IS INTENDED ONLY FOR THE ADDRESSEE and may contain confidential and privileged information. If the reader of this message is not the intended recipient, you are notified that any dissemination, distribution or copy of this communication is strictly Prohibited. If you have received this message by error, please notify us immediately, return the original mail to the sender and delete the message from your system." |
From: Brian D. <br...@dl...> - 2002-03-07 22:21:07
|
perhaps i should give a more concrete example. i have this utility method to stick a bunch of objects in a database. each object corresponds to a table row and "knows" how to read/write itself into a table. // pseudojava public void update(Connection conn, SQLObject[] objs) { for (each obj in objs) { if (obj.isMarkedDeleted()) { // jdbc code to delete obj from table } else if (obj.isMarkedChanged()) { // jdbc code to update table row for obj } else if (obj.isMarkedNew()) { // jdbck code to insert obj into table } else { // do nothing } } } now i want to test this method. so i write something like: public void testUpdate throws Exception { // object declarations and creation snipped obj1.markNew(); obj2.markChanged(); obj3.markDeleted(); mockStatement.setExpectedExecuteCalls(3); dbUtility.update(mockConnection, new SQLObject[] { obj1, obj2, obj3 }); mockStatement.verify(); } hmm. the above doesn't actually test that i did one insert, one delete, and one update. so i could do the three updates separately -- one for each object -- and test the query string each time: public void testUpdate throws Exception { // object declarations and creation snipped obj1.markNew(); obj2.markChanged(); obj3.markDeleted(); mockStatement.setExpectedExecuteCalls(1); mockStatement.setExpectedQueryString( "INSERT INTO objecttable values ('this is painful')"); dbUtility.update(mockConnection, new SQLObject[] { obj1 }); mockStatement.verify(); mockStatement.setExpectedExecuteCalls(1); mockStatement.setExpectedQueryString("UPDATE objecttable set ..."); dbUtility.update(mockConnection, new SQLObject[] { obj2 }); mockStatement.verify(); ... etc. ... } this is better, although now i'm not testing my ability to do all three at once. but now i decide to change my implementation of update() so that instead of calling executeUpdate() on a Statement, it gets a ResultSet, and goes through it updating and deleting and inserting rows as appropriate. oops. my tests just broke. i'm not sure what to think about this. since there is always more than one way to do an update in JDBC, it seems to follow that there is no implementation-INdependent way to test a method which is supposed to perform some set of JDBC updates depending on the state of something-or-other, if the only tools you have are tools that test which particular JDBC methods or SQL queries you performed. i don't see the point of writing implementation-dependent tests. i want to test the input-output behavior of my methods, not how they function. comments? -brian |