Most unit testing tutorials/examples out there usually involve defining the data to be tested for each individual test. I guess this is part of the "everything should be tested in isolation" theory.
However I've found that when dealing with multitier applications with a lot of DI, the code required for setting up each test gets very long winded. Instead I've built a number of testbase classes which I can now inherit which has a lot of test scaffolding pre-built.
As part of this, I'm also building fake datasets which represent the database of a running application, albeit with usually only one or two rows in each "table".
Is it an accepted practice to predefine, if not all, then the majority of the test data across all the unit tests?
Update
From the comments below it does feel like I'm doing more integration than unit testing.
My current project is ASP.NET MVC, using Unit of Work over Entity Framework Code First, and Moq for testing. I've mocked the UoW, and the repositories, but I'm using the real business logic classes, and testing the controller actions. The tests will often check that the UoW has been committed, e.g:
[TestClass]
public class SetupControllerTests : SetupControllerTestBase {
[TestMethod]
public void UserInvite_ExistingUser_DoesntInsertNewUser() {
// Arrange
var model = new Mandy.App.Models.Setup.UserInvite() {
Email = userData.First().Email
};
// Act
setupController.UserInvite(model);
// Assert
mockUserSet.Verify(m => m.Add(It.IsAny<UserProfile>()), Times.Never);
mockUnitOfWork.Verify(m => m.Commit(), Times.Once);
}
}
SetupControllerTestBase
is building the mock UoW, and instantiating the userLogic
.
A lot of the tests require having an existing user or product in the database, so I've pre-populated what the mock UoW returns, in this example userData
, which is just an IList<User>
with a single user record.
Best Answer
Ultimately, you want to write as little code as possible to get as much outcome as possible. Having a lot of the same code in multiple tests a) tends to result in copy-paste coding and b) means that if a method signature changes you can end up having to fix a lot of broken tests.
I use the approach of having standard TestHelper classes that provide me with a lot of the data types that I routinely use, so I can create sets of standard entity or DTO classes for my tests to query and know exactly what I will get each time. So I can call
TestHelper.GetFooRange( 0, 100 )
to get a range of 100 Foo objects with all their dependent classes/fields set.Particularly where there are complex relationships configured in an ORM type system which need to be present for things to run correctly, but aren't necessarily significant for this test that can save a lot of time.
In situations where I'm testing close to the data level, I sometimes create a test version of my repository class that can be queried in a similar way (again this is in an ORM type environment, and it wouldn't be relevant against a real database), because mocking out the exact responses to queries is a lot of work and often only provides minor benefits.
There are some things to be careful of, though in unit tests: