Integration Testing: Am I doing it right?
Here's an integration test I wrote for a class that interacts with a database:
[Test]
public void SaveUser()
{
// Arrange
var user = new User();
// Set a bunch of properties of the above User object
// Act
var usersCountPreSave = repository.SearchSubscribersByUsername(user.Username).Count();
repository.Save(user);
var usersCountPostSave = repository.SearchSubscribersByUsername(user.Username).Count();
// Assert
Assert.AreEqual(userCountPreSave + 1, userCountPostSave);
}
开发者_运维技巧It seems to me that I can't test the Save
function without involving the SearchSubscriberByUsername
function to find out if the user was successfully saved. I realize that integration tests aren't meant to be unit tests which are supposed to test one unit of code at a time. But ideally, it would be nice if I could test one function in my repository class per test but I don't know how I can accomplish that.
Is it fine how I've written the code so far or is there a better way?
You have a problem with your test. When you're testing that data is saved into the database, you should be testing that it's in the database, not that the repository says that it's in the database.
If you're testing the functionality of repository, then you can't verify that functionality by asking if it has done it correctly. It's the equivalent of saying to someone 'Did you do this correctly?' They are going to say yes.
Imagine that repository never commits. Your test will pass fine, but the data won't be in the database.
So, what I would do is to to open a connection (pure SQL) to the database and check that the data has been saved correctly. You only need to a select count(*) before and after to ensure that the user has been saved. If you do this, you can avoid using the SearchSubscribersByUsername as well.
If you're testing the functionality of repository, you can't trust repository, by definition.
To unit test something like a "Save" function, you will definitely need some trustworthy channel to check the result of the operation. If you trust SearchSubscribersByUsername
(because you have already made some unit tests for that function on its own), you can use it here.
If you don't trust SearchSubscribersByUsername
and you think your unit test could also break because there is an error in that function (and not in Save
), you should think about a different channel (perhaps you have a possibility to make a bypassing SQL access to your DB to check the Save
result, which may be simpler than the implementation of SearchSubscribersByUsername
)? However, do not reimplement SearchSubscribersByUsername
again, that would be getting pointless. Either way, you will need at least some other function you can trust.
Unless the method you are testing returns definitive information about what you have done I don't see any way to avoid calling other methods. I think you are correct in your assumption that Integration testing needs a different way of thinking from Unit testing.
I would still build tests that focus on individual methods. So in testing Save() I may well use the capabilities of Search(), but my focus is on the edge cases of Save(). I build tests that deal with duplicate insertions or invalid input data. Then later I build a whole raft of Search() tests that deal with the edge cases of Search().
Now one possible way of thinking is that Save and Search have some commonality, a bug in Search might mask a bug in Save. Imagine, for example, if you had a caching layer down there. So possibly an alternative approach is to use some other verification mechanism. For example a direct JDBC call to the database, or alteratively introducing mocking layers at some point in your infrastructure. When building complex Integrated Systems this kind of "Back Door" verification may be essential.
Personally, I've written countless tests very similar to this and think its fine. The alternative is to stub out the database so that searchSubscribers never actually does anything but thats great deal of work for what I'd say is little gain.
精彩评论