How might I go about writing a unit test which will test the ability of a method to add a record to a database? Currently, I have the following:
[TestMethod]
public void AddUserTest()
{
Boolean expected = true;
Boolean result = UserManager.AddUser(test);
Assert.AreEqual(expected, result);
}
This works appropriately if I'm only testing the ability to add a record to the database (without worrying if the record already exists). However, I'm not sure how to author the test such that it will still pass if the submission fails due to a pre-existing record.
If it makes a difference, I'm using LIN开发者_开发技巧Q to SQL for my database transactions. From what I could gather in the MSDN Documentation, DataContext.SubmitChanges()
has no return value, so I'm also unsure how to determine if a particular transaction was successful.
I'll keep looking through the documentation. Perhaps DataContext.SubmitChanges()
throws an exception upon record conflict or other failure that I could be catching in the unit test?
As soon as an external agent is present (such as file system, database etc.) it's really an integration test.
Your AddUserTest
above is flawed: the AddUser
method could return true
without adding anything or not adding it correctly and still return true! In that case, you have not accurately tested anything.
Write an integration test that adds data to the database, then retrieves it and compares the two sets of values for equality.
I agree with Mitch (this is really an integration test).
To handle your scenario you can add an ExpectionException
attribute to the test. This means that is you receive the expected exception then the test still passes.
[TestMethod()]
[ExpectedException(typeof(System.Data.Linq.DuplicateKeyException))]
public static void MyTest()
{
....
}
If you need to check the message of the exception, there is a useful overload that allows you to specify the expected string (see http://msdn.microsoft.com/en-US/library/ms243315.aspx)
public ExpectedExceptionAttribute(
Type exceptionType,
string noExceptionMessage
)
which would be used
[ExpectedException(typeof(DuplicateKeyException), "Something went wrong")]
If you start a transaction before the test runs and then abort the transaction after the test runs then you will never have pre-existing data rows.
Use [TestInitialize]
and [TestCleanup]
for before and after run; respectively.
See http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx
You'll get a primary key violation exception thrown at you if the record exists. You could avoid the exception by changing UserManager.AddUser to check for this case explicitly by selecting the record out of the database before insert (see Linq's Any() or SingleOrDefault()). If a record comes back, explicitly return false.
Or wrap all the code in AddUser in a try/catch block. Return false in the catch.
Heck, do both.
Also, why not check the test database to see if the record was inserted in the test?? I wouldn't take the method under test's word for success/failure, shouldn't you verify this?
Unit testing with a real database is not encouraged by unit testing purists. We actually unit test most of our DB layer's LinqToSql code with a mock database. Google "Irepository linq to sql unit test" for a bunch of results. I took a bunch of the ideas from the slew of blog posts that will be returned in that search and made a system that's worked out surprisingly well (but not without some challenges).
精彩评论