There’s two things you frequently hear when talking to developers about TDD. It’s either "our code is too complicated, you can’t separate it" or "how can you test a database" (or graphics system, or insert other complicated system).
Here’s an example of code that someone might have in mind.
public class User { private DBConnection connection; public string Name { set { SetName(value); } } private void SetName(string name) { this.name = name; // other stuff… connection.ExecuteSQL("update users set name = ‘" + name + "’ where id = " + id); } }
So, usually someone will want to create a mock object for the database for testing purposes. That’s one way to do it. But, I’m not sure it’s the best way. After all, you want to test your class, not your understanding of the database!
There’s another issue here. The User class knows waaaaay too much about the database for its own good. I mean, what if we wanted to save to a file system? We can’t, at least without serious rework. While we could replace the database connection with a different type, we’re still tightly coupling to the idea of a database.
What we really want to do is save the user info. So let’s just make a method that does that.
private void SetName(string name) { this.name = name; // other stuff… SaveUser(); } private void SaveUser() { connection.ExecuteSQL("update users set name = ‘" + name + "’ where id = " + id); }
Okay, that’s a little better. Now we’ve removed the implementation details of saving the user from the SetName method. It now pretty much says what, conceptually, we want to do. All the database stuff is in its own method.
But, being in its own method doesn’t really allow us to change anything. So, let’s extract an interface.
public interface IUserPersistence { void SaveUser(int id, string name); } IUserPersistence persistence; private void SetName(string name) { this.name = name; // other stuff… persistence.SaveUser(id, name); }
And now, our user class doesn’t even have to know about a database at all, or maintain a reference to its connection. Instead of giving it a DBConnection, we just give it a IUserPersistence object. We can then test away to our heart’s content, throw whatever exceptions we want, without having to worry about trying to mock anything as heavyweight as a whole database. We also remove all database specific code from our user class, allowing it to be reused elsewhere.
It’s also conceptually cleaner - the user class now only has to know about the idea of saving itself, not the specific details.
The drawback of this is that it does potentially pollute the API space a bit. One way to get around this is to set the instance of the interface to protected, and not expose them on the default object. Then, you can write a test object which inherits from the normal class which exposes a setter.
So, the answer to "how do I mock a database" is simple - you don’t.
Post a Comment
You must be logged in to post a comment.