No external test harness is required, it doesn't rely on having a Repository pattern being implemented and you don't need to buy any new products. Just Visual Studio 2010 Professional Edition, Premium Edition or Ultimate Edition out of the box.
Terminology |
---|
The EDMX Code Generation Item used in this article generates two ObjectContext classes. When we refer to the Vanilla ObjectContext, this is the same strongly typed ObjectContext that is generated from your EDMX by default. The Mocking ObjectContext we refer to is another context used only for unit testing, which implements the same strongly typed interface, and is also generated from your EDMX file. More details on the architecture of the artifact generator are provided here. |
Step 1: Set Up the Code Generation Item
These steps will change your EDMX Code Generation Item to the ADO.NET Mocking Context Generator. This is what makes your C# code from your Entity Data Model (EDMX) file.- Open your EDMX file, right click the background, and from the context menu select Add Code Generation Item.
- Select Online Templates->Database->ADO.NET Mocking Context Generator:
- Open your EDMX file, right click the background, and from the context menu select Properties.
- In the Properties change Code Generation Strategy to None. This will turn off the default Code Generation Item.
Step 2: Ensure "Context Agnostic" Code
Context Agnostic means that your Business Logic code shouldn't be aware of what context class it is using. We need to ensure that any hard dependencies on your generated Vanilla ObjectContext are replaced with references to your new generated ObjectContext Interface instead.Identify Context "Gnostic" Code
We need to find anywhere in your Business Logic Layer that references the vanilla concrete ObjectContext:class MovieLogic { private MoviesContext _context; // Hard dependency on vanilla ObjectContext public MovieLogic() { _context = new MoviesContext(); // Hard dependency on vanilla ObjectContext } }
Option 1: Delegate responsibility to the client
To remove the hard dependency, the simplest option is to ensure all ObjectContext references use the ObjectContext Interface instead, and take on the ObjectContext instance in their constructor:class MovieLogic { private IMoviesContext _context; // Interface used instead: context agnostic public MovieLogic( IMoviesContext context ) { _context = context; // Instantiation responsibility is delegated to caller } }The Business Logic Client can then be re-engineered to instantiate a vanilla ObjectContext object for us when the Business Logic object is instantiated:
MovieLogic logic = new MovieLogic( new MoviesContext() );
Note |
---|
Your application will never instantiate a Mocking Context, as this is only used when testing your business logic in your Unit Test Project. |
Option 2: Delegate responsibility to a factory
If your Business Logic objects are instantiated all over the place, then it might be easier to contain the instantiation responsibility within a factory, and call the factory when a context is needed from the Business Logic objects themselves:class MovieLogic { private IMoviesContext _context; public MovieLogic() { _context = MyContextFactory.CreateContext(); // Instantiation responsibility is delegated to factory } }
Implementing the Factory |
---|
Whilst no implementation of an example factory is given here, the factory just needs to return a new instance of your Vanilla ObjectContext, unless the "testing" flag is raised, when a Mocking ObjectContext will be instantiated instead. |
Step 3: Create the Unit Tests
To create the unit tests in Visual Studio, a new test project needs to be created. This project will contain the Unit Test class that runs all the tests, and the code required to set up the mocking context, mock data, and the calls to the business logic.- Right click your solution, and select Add -> New Project
- From the Add New Project Dialog select Visual C# -> Test -> Test Project:
- Add a Reference in your new test project to your Business Logic Project.
- Add a Reference to System.Data
- Add a Reference to System.Data.Entity
Set up the test "database"
This step creates a fresh mocking context before every test is executed. The mocking context is populated with some sample data that the tests can use. Additionally any test can set up its own data as part of its testing.- In your new test project, open your Unit Test code file (eg. "UnitTest1.cs")
- Add a member (eg.
_context
) to your unit test class, of the type of your ObjectContext Interface. The name of the interface will be shown if you expand the *.Context.tt file in the project containing your EDMX file:
private IMoviesContext _context;
[TestInitialize] public void TestInitialize() { _context = new MoviesContextMock(); Actor[] actors = new Actor[] { new Actor() { Id = 1, Name = "Robert De Niro" }, new Actor() { Id = 2, Name = "Ray Liotta" }, new Actor() { Id = 3, Name = "Joe Pesci" }, new Actor() { Id = 4, Name = "Jodie Foster" } }; Movie[] movies = new Movie[] { new Movie() { Id = 1, Title = "Goodfellas", ReleaseYear = 1990, Rating=5 }, new Movie() { Id = 2, Title = "Taxi Driver", ReleaseYear = 1976, Rating=5 }, }; movies[ 0 ].Cast.Add( actors[ 0 ] ); movies[ 0 ].Cast.Add( actors[ 1 ] ); movies[ 0 ].Cast.Add( actors[ 2 ] ); movies[ 1 ].Cast.Add( actors[ 0 ] ); movies[ 1 ].Cast.Add( actors[ 3 ] ); actors.ToList().ForEach( actor => _context.Actors.AddObject( actor ) ); movies.ToList().ForEach( movie => _context.Movies.AddObject( movie ) ); }
Implement Test Methods
Create some functions on your new unit test class, with the attribute TestMethod. These will execute the individual unit tests for your business logic.Use the Assert.* methods to check the results of calls to your business logic. These calls will be evaluated by the test engine.
[TestMethod] public void TestGetMovieByTitle() { MovieLogic logic = new MovieLogic( _context ); Movie movie = logic.GetMovieByTitle( "Goodfellas" ); Assert.AreEqual( 1, movie.Id ); } [TestMethod] public void TestGetMovieByTitleBad() { MovieLogic logic = new MovieLogic( _context ); Movie movie = logic.GetMovieByTitle( "Arial the Little Mermaid" ); Assert.AreEqual( null, movie ); } [TestMethod] public void TestGetMovieByReleaseYear() { MovieLogic logic = new MovieLogic( _context ); Movie[] movies = logic.GetMovieByReleaseYear( 1976 ); Assert.AreEqual( 1, movies.Length ); Assert.AreEqual( "Taxi Driver", movies[ 0 ].Title ); }
Running the Tests
To run the tests, select Test -> Run -> All Tests In Solution.The Test Results panel will pop up, and give you the results of your tests:
Frequently Asked Questions
Where's my Vanilla ObjectContext's AddToObjectSetName method gone?
This method has now been deprecated in Entity Framework 4.0. Instead of usingAddToObjectSetName
, use <My Vanilla ObjectContext>.<ObjectSetName>.AddObject()
.