From: James H. <jw...@al...> - 2003-04-17 23:17:13
|
I'm just starting to experiment with using MockObjects to test classes which access a database. I have some simple methods which I've been able to test, but now I'm trying to test some more complicated methods. In particular I'm testing a method which uses another method to get a result which is then input into a second query. It looks something like this (a rough approximation of the algorithm): public double value() { int count = fullCount(); // this method peforms a count(*) type of query String query = "blah blah blah limit " + (count * .30); mockResults = mockStatement.executeQuery(query); if (mockResults.next()) return mockResults.getDouble(1); return Double.NaN; } The problem is testing this method. I'm doing two different queries so I need to get two different result sets. I'm sure I can come up with some way to handle this, but I'm wondering if there is a general pattern for handling this sort of thing? What bothers me is that in order to test my "value" method, I also have to know how to set up the environment to support methods that the method might call, and so on down the line. Is there a better way to organize my database code which might avoid this problem? Thanks. -- James Howe |
From: Steve F. <st...@m3...> - 2003-04-18 08:41:03
|
First the simple bit. Assuming you're using the same statement object, you can just add another result set to be returned. I'm not quite sure what you're unhappy about with the test. Could you post an example of the sort of test you want to write? The short answer is that this sort of testing tends to push you towards writing smaller, more focussed classes and methods which some of us believe is a Good Thing. S. James Howe wrote: > I'm just starting to experiment with using MockObjects to test classes > which access a database. I have some simple methods which I've been > able to test, but now I'm trying to test some more complicated methods. > In particular I'm testing a method which uses another method to get a > result which is then input into a second query. It looks something like > this (a rough approximation of the algorithm): > > public double value() { > int count = fullCount(); // this method peforms a count(*) type of > query > String query = "blah blah blah limit " + (count * .30); > mockResults = mockStatement.executeQuery(query); > if (mockResults.next()) > return mockResults.getDouble(1); > return Double.NaN; > } > > The problem is testing this method. I'm doing two different queries so > I need to get two different result sets. I'm sure I can come up with > some way to handle this, but I'm wondering if there is a general pattern > for handling this sort of thing? What bothers me is that in order to > test my "value" method, I also have to know how to set up the > environment to support methods that the method might call, and so on > down the line. Is there a better way to organize my database code which > might avoid this problem? > > Thanks. > > -- "A LISP programmer knows the value of everything but the cost of nothing. A C programmer knows the cost of everything but the value of nothing." (Todd Proebsting) |
From: James H. <jw...@al...> - 2003-04-18 13:39:53
|
Ok, here's an example of what I ran into yesterday. First, I have a class which has a method on it to return the count of a specific query. The code to do the count looks like this: public int regionCount() throws SQLException { int regionCount = 0; Statement statement = databaseConnection.createStatement(); final String query = regionCountSQLPrefix() + regionCountCountryClause() + REGION_COUNT_SQL_POST; ResultSet results = statement.executeQuery(query); if (results.next()) regionCount = results.getInt(1); statement.close(); return regionCount; } I wrote a test to test this method. First I created the setup method: public void setUp() { mockConnection = new MockConnection(); mockStatement = new MockStatement(); mockConnection.setupStatement(mockStatement); workbench = new CreditGradesWorkbench(countryCodes, mockConnection); } then the test method itself: public void testRegionCount() throws SQLException { final Integer[] resultObject = { new Integer(REGION_COUNT) }; final CommonMockSingleRowResultSet mockResultSet = new MockSingleRowResultSet(); mockResultSet.addExpectedIndexedValues(resultObject); mockConnection.setExpectedCreateStatementCalls(1); mockStatement.setExpectedExecuteCalls(1); mockStatement.setExpectedCloseCalls(1); mockStatement.addResultSet(mockResultSet); mockStatement.setExpectedQueryString(expectedRegionCountQueryString()); int result = workbench.regionCount(); assertEquals(REGION_COUNT, result); mockStatement.verify(); mockConnection.verify(); } I have another method in my class which returns a value from a query, where part of the query uses the regionCount returned by the other method. This method looks like this: public double regionCreditGradeForPercentile(int percentile, int regionCount) { try { int regionCount = regionCount(); double creditGrade = 0.0; Statement statement = databaseConnection.createStatement(); final String query = regionCreditGradeSQLPrefix() + regionCountCountryClause() + REGION_COUNT_SQL_POST + " limit " + percentileCount(percentile, regionCount); ResultSet results = statement.executeQuery(query); if (results.next()) creditGrade = results.getDouble(1); statement.close(); return creditGrade; } catch (SQLException e) { // log something here return Double.NaN; } } Where I ran into trouble was when I tried to write the test for this method. Originally the test method looked like this: public void testRegionCreditGradeForPercentile() { final Double[] resultObject = { new Double(CREDITGRADE) }; final CommonMockSingleRowResultSet mockResultSet = new MockSingleRowResultSet(); mockResultSet.addExpectedIndexedValues(resultObject); mockConnection.setExpectedCreateStatementCalls(1); mockStatement.setExpectedExecuteCalls(1); mockStatement.setExpectedCloseCalls(1); mockStatement.addResultSet(mockResultSet); mockStatement.setExpectedQueryString(baseExpectedRegionCreditGradeQueryString() + " limit " + workbench.percentileCount(10, REGION_COUNT)); double result = workbench.regionCreditGradeForPercentile(10); assertEquals(CREDITGRADE, result, 0.001); mockStatement.verify(); mockConnection.verify(); } However the test failed because the expected query did not match the real query. The problem with my setup is that the method I'm calling, regionCreditGradeForPercentile, makes a call internally to the regionCount method. The region count method needs the statement setup to return the REGION_COUNT value, this value is then appended to the query used by the regionCreditGradeForPercentile which does another query and should return the CREDITGRADE value as the result. Is this just a matter of making sure that I add the value that the regionCount method would return to the result set before adding the value that the regionCreditGradeForPercentile method returns? I assume I would also have to change the values on the number of expected execute calls, close calls, etc. The issue I have is I want to do things once and only once, but I also want to keep things simple. I want to be able to set up the test environment for the regionCreditGradeForPercentile without having to dive into the details of the regionCount method that it invokes. Besides, then I feel like I'm testing regionCount twice. I could redesign the regionCreditGradeForPercentile method to take both the percentile value and a regionCount, but that doesn't quite feel right either. Like I said in my original message, I'm just trying to get a feel for the best way to write tests for code which accesses information in a database. Currently (in another project) we have unit tests for code which actually goes to the database to retrieve information. In most cases I'm sure that a real database wouldn't be necessary, but I'm sure we would need to redesign to make the code more testable. I'm just looking for any best practices that people may have developed for this sort of thing. (BTW, I did read the document on www.mockobjects.com concerning JDBC testing) Thanks. On Fri, 18 Apr 2003 09:41:37 +0100, Steve Freeman <st...@m3...> wrote: > First the simple bit. Assuming you're using the same statement object, > you can just add another result set to be returned. > > I'm not quite sure what you're unhappy about with the test. Could you > post an example of the sort of test you want to write? The short answer > is that this sort of testing tends to push you towards writing smaller, > more focussed classes and methods which some of us believe is a Good > Thing. > > S. > > James Howe wrote: >> I'm just starting to experiment with using MockObjects to test classes >> which access a database. I have some simple methods which I've been >> able to test, but now I'm trying to test some more complicated methods. >> In particular I'm testing a method which uses another method to get a >> result which is then input into a second query. [...] > -- James Howe |