Test-driven development not working for my class
I have this class that I wanted to build using TDD, but I failed. It's a pretty basic class called SubMissions
, and all it does is it fetches some data from an SQL database.
So it has methods开发者_如何学JAVA like getSubMissionForPage()
, getSubMissionFromId()
etc..
I tried building it using TDD. My first test contained a call to getSubMissionPage()
, which only purpose is to return data. So making this test fail is pretty darn hard, since it can return any data, I couldn't come up with a way to make it fail.
I know making your test fail is the first step into knowing what to implement, but what do you do when there's actually no way of failing a test?
Any time you're relying on an external data source, your test can always fail. What if the connection is dropped to your DB? What if the table doesn't exist that you're trying to get data from? What if the data you get back is not the data you expect?
Testing classes that connect to DBs is a little more tricky than testing HelloWorld.java, because you need some way to mock the DB. You can learn more about Mock Objects here.
Short answer, your tests/software can ALWAYS fail. If you don't think your test can fail, you're not thinking hard enough about the problem space.
Retrieval from a database isn't the place to start with TDD.
You might do well to look at some examples of TDD on the net. Bob Martin's bowling game scoring is a fun place to start.
That being said ...
My first test contained a call to getSubMissionPage(), which only purpose is to return data. So making this test fail is pretty darn hard, since it can return any data, I couldn't come up with a way to make it fail.
The purpose isn't to return data, but to return the correct data.
The way to make a test for this is to supply it with a database that should make it return a specific result and see that it does. And of course it won't until you write the correct code, so you write the test first and the real implementation after seeing it fail.
The hard part with TDD involving a database is that testing with a real database can be slow and making the test repeatable can be difficult as your tests will sometimes change the data. To deal with these issues, you can get help from tools like DbUnit, mock JDBC, use an in-memory database, and use rollbacks to make sure your tests don't make permanent changes.
But you're best off not starting with database stuff.
The beauty of TDD is that if you're finding a test hard to write, it points to a potential problem in design. In this case, the abstraction of your data access logic.
For example, you could use Dependency Injection to send in an interface to a data access repository (using a fake or mock in your test case) that will let you isolate your data access tests from your logic tests. i.e. build a SubmissionRepository that handles the data access, and your submissions class handles the business logic, going to your repository for the basic crud and shaping operations.
Naturally at some point you'll need to test some data access as part of an integration test, but I always use the exercise of starting with TDD to help drive more decoupled designs.
Have the method throw a RuntimeException if you want the test to fail. Eclipse will fill in method stubs with a suitable UnsupportedOperationException for you.
Then to make the test go green, simply have the method return null instead of throwing.
Don't bother connecting to the database at all until an extra test or two force you to.
Well. there are a few standard type things to test for: null and and empty string spring to mind. If the data can truly be anything, then anything is valid.
Starting with testing a database is probably not the best way to dive into TDD. Start with a class that has no dependencies. Imagine a chess game, you might have classes like:
- Board
- Square
- Location (the row, col of the square on the board)
I would start with Location since it has no dependencies, then Square since it only depends on Location, then Board since it only depends on Square.
‘getSubMissionPage‘ is not supposed to just return any data--it's supposed to make a specific request and then return somehing based on the request.
You need to configure ‘SubMissions‘ to use a mock data source and then test that the class makes the corrext requests of it and maps the data into objects correctly.
You don't have to write a test for every single method. If you think a test is too simple to break, you can decide not to write a test for it. Some will argue that you should still write a test, but I think there are cases where you don't need to.
Take a look at this JUnit FAQ: http://junit.sourceforge.net/doc/faq/faq.htm#best_3
- modify your class to throw any runtime exception and see that test fails.
- mofify your test to expect exception and see that test fails
(1) and (2) were just exercises that should help you to be sure that the test is working.
Now ask yourself question: what could be the reason for test failure? I think that in your case the reasons list looks like: 1. no connection to DB 2. wrong DB schema 3. inconsistent data
Here are the ways to simulate these reasons: 1. shut down your DB or change credentials/jdbc URL you are using to connect. 2. drop interesting table or change name(s) of its columns. 3. it is a bit hard to make data inconsistent. This typically happens when DB constraints are not defined correctly. Sometimes people do it to make schema more generic. If it is not your case just forget about #3.
Good luck with TDD! I believe that in future your testing scenarios will become more sophisticated.
精彩评论