|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionNeed:Lets start with an example, suppose we are writing unit tests for following two cases:
Initial state of database is like
We run first unit test case for GetCustomersByIdGreaterThan() [Test] public void CanGetCustomersByIdGreaterThan() { const int CUSTOMER_ID = 1; const int EXPECTED_COUNT = 4; IList<Customer> customers = provider.GetCustomersByIdGreaterThan(CUSTOMER_ID); if (customers.Count <= 0) Assert.Warning("You are not testing what you want to do."); foreach (Customer customer in customers) { Assert.GreaterThan(customer.CustomerId, CUSTOMER_ID); } Assert.AreEqual(EXPECTED_COUNT, customers.Count); } Result: This will pass as there are 4 records whose CustomerId is greater than 1. Now, we run unit test for AddCustomer() [Test] public void CanAddCustomer() { Customer customer = new Customer(); customer.FirstName = "Atul"; customer.LastName = "Joshi"; //Customer customer = new Customer() { FirstName = "Atul", LastName = "Joshi" }; int newCustomerId = provider.AddCustomer(customer); Customer testCustomer = provider.GetCustomerById(newCustomerId); Assert.IsNotNull(testCustomer); } Result: This will also pass as we are adding customer and getting it back with CustomerId. Till this stage everything is fine.
Some, immediate fixes/solution to this scenario are:
Drawbacks of this approach:
Conclusion: You are not always sure your unit tests will pass successfully.
What the article/code snippet does, why it's useful, the problem it solves etc. SolutionBefore going into details of solution, lets look at the basic component which forms the base of solution. It's NDbUnit Framework. NDbUnit:
For details about NDbUnit look at the following links: http://www.ndbunit.org/ - Link for main site. http://www.ndbunit.org/NDbUnitInActionTutorial/NDbUnitInActionTutorial_viewlet_swf.html - Tutorial movie. http://www.ndbunit.org/ndoc/NDbUnit.Core.html - NDBUnit API documentation list. Some basic things that are related to our solution are:
This structure in solution will look like:
Utility class Now, next step is creating a Utility class which will do functionality of restoring database state using NDbUnit.
Lets look at some important class components briefly: Properties:
Methods:
Rest of the methods are helper methods. Most of the methods and properties are virtual so that one can override them to provide their own implementation. Using the codeSimple enough. Inherit unit test class with DatabaseUnitTestBase and add following method to class: /// <summary> /// Inherit unit test class with unit test utility class. /// </summary> [TestFixture] public class HibernateDataProviderTest : UnitTest.DatabaseUnitTestBase { DataAccessLayer.HibernateDataProvider provider = null; /// <summary> /// This method called once before unit test execution starts. /// It is responsible for creating backup (BackupData.xml) file for database. /// </summary> [TestFixtureSetUp] public void TextFixtureSetup() { DatabaseFixtureSetup(); // This method from utility class created database backup file. provider = new DataAccessLayer.HibernateDataProvider(); } /// <summary> /// This method called once after unit test execution completes. /// It is responsible for restoring backup (BackupData.xml) file to database. /// </summary> [TestFixtureTearDown] public void TestFixtureTearDown() { DatabaseFixtureTearDown(); // This method from utility class restores database backup. } /// <summary> /// This method called before execution of each unit test. /// It is responsible for restoring test data (TestData.xml) file to database. /// </summary> [SetUp] public void Setup() { DatabaseSetUp(); // This method from utility class restores test data for unit test execution. } [TearDown] public void TearDown() { DatabaseTearDown(); } Following these methods add your unit tests. So, now
Now, lets revisit our problem of failing GetCustomersByIdGreaterThan() unit test: State of Customer table before execution of AddCustomer() unit test:
Debug code for AddCustomer() test case, to check intermediate state of database:
Highlighted rows shows newly added customer. So this unit test will pass successfully. After execution of unit test, state of Customer table is:
So, now if we execute GetCustomersByIdGreaterThan() unit test, it will also pass as database state is restored successfully!! Bingo!! This magic is happening because of NDbUnit, utility class and [TestFixture], [TestTearDown], [Setup], [TearDown] methods from Unit test. Now, you can write unit tests for which they are intended to be! Download code for Utility Class from here. Download code for sample solution from here. Happy Programming!!
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||