Image Image Image Image Image
Scroll to Top

To Top

Clean Code Developer Here I share my ideas about software craftsmanship.

Walkthrough: ADO.NET Unit Testable Repository Generator

This walkthrough is going to show you the usage of my new T4 template called „ADO.NET Unit Testable Repository Generator“.

Table of contents:

My demo implements a small repository to manage simple text-snippeds in the database. I will start with an empty ASP.NET MVC 2 project. Any other type would work, too.


Step 1: Create the Entity Data Model

Now we are going to add an Entity Data Model (EDMX) file.

  • Right click on Models, select Add > New Item…
  • and choose ADO.NET Entity Data Model. I recommend the Name: “WebNoteModel.edmx

  • Generate it from the database,

  • and create a working connection string with the help of the wizard.
  • Please be sure that you select a meaningful Entity Container Name. This name will be used for the generated files, too. I chose “FileStore”.

  • Select your tables and Pluralize or singularize generated object names (select the checkbox)
  • and chose a name for the Namespace. In my case: „WebNoteModelDatabase“.

Step 2: Add the Code Generation Item

All right. This was standard stuff. We are now going to include the T4 template.

  • Open the created EDMX file, right click the background, and from the context menu select Add Code Generation Item and chose the item “ADO.NET Unit Testable Repository Generator”. You could use the name “WebNoteModel.tt” here. (Note: the screenshot is outdated, I renamed the extension. The previous name was: „ADO.NET Mocking Context Generator – Extended“)

  • The T4 templates have generated some new files. This includes all the required POCOs (Plain Old CLR Object), a simple ObjectContext and an Interface that should be used to access it. If you get compilation errors, then you should reference the following assemblies:
    1. Microsoft.Practices.Unity (download at http://unity.codeplex.com)
    2. Microsoft.VisualStudio.QualityTools.UnitTestFramework (included with Visual Studio)

Step 3: Use the Repository

To start coding you just have to create a partial class with the name of the entity container and the suffix “Repository”. (eg. “public partial class WebNoteRepository”). The following sample shows all CRUD (Create, Read, Update and Delete) operations on the Entity “Note”.

namespace WebNoteMvc.Models
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
 
    /// <summary>
    /// Sample Repository
    /// </summary>
    public partial class WebNoteRepository
    {
        /// <summary>
        /// Gets all notes from DB
        /// </summary>
        /// <returns>all notes from DB</returns>
        public IEnumerable<Note> GetAllNotes()
        {
            return this.Context.Notes.ToList();
        }
 
        /// <summary>
        /// Gets one note from DB
        /// </summary>
        /// <param name="id">The id of the note to show.</param>
        /// <returns>notes from DB</returns>
        public Note GetNote(int id)
        {
            Note noteToShow = (from n in this.Context.Notes
                               where n.NoteId == id
                               select n).FirstOrDefault();
 
            return noteToShow;
        }
 
        /// <summary>
        /// Adds the a new note to DB.
        /// </summary>
        /// <param name="noteToAdd">The note to add.</param>
        public void AddNote(Note noteToAdd)
        {
            noteToAdd.Added = DateTime.Now;
            this.Context.Notes.AddObject(noteToAdd);
            this.Context.SaveChanges();
        }
 
        /// <summary>
        /// Edits one note note.
        /// </summary>
        /// <param name="noteData">note data.</param>
        public void EditNote(Note noteData)
        {
            Note noteToEdit = this.GetNote(noteData.NoteId);
 
            if (noteToEdit == null)
            {
                return;
            }
 
            noteToEdit.Title = noteData.Title;
            noteToEdit.Message = noteData.Message;
            this.Context.SaveChanges();
        }
 
        /// <summary>
        /// Deletes one note note.
        /// </summary>
        /// <param name="id">The id of the note to delete.</param>
        public void DeleteNote(int id)
        {
            Note noteToDelete = this.GetNote(id);
 
            if (noteToDelete == null)
            {
                return;
            }
 
            this.Context.Notes.DeleteObject(noteToDelete);
            this.Context.SaveChanges();
        }
    }
}

Step 4: Use the unit test

The following example shows a simple unit test. The T4 template has generated a base class for you. You just have to inherit from the XBaseRepository-class. The class requires you to implement the abstract methods that should return a list of Entities. These Entities “simulate” a real database.

namespace WebNoteMvc.Tests.Models
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
 
    using Microsoft.VisualStudio.TestTools.UnitTesting;
 
    using NUnit.Framework;
 
    using WebNoteMvc.Models;
    using WebNoteMvc.Models.WebNoteUnitTest;
 
    using Assert = NUnit.Framework.Assert;
    using IgnoreAttribute = Microsoft.VisualStudio.TestTools.UnitTesting.IgnoreAttribute;
 
    /// <summary>
    /// Example test for the WebNoteRepository
    /// </summary>
    [TestClass]
    public class WebNoteRepositoryTest : WebNoteBaseRepositoryTest
    {
        [TestMethod]
        public void GetAllNotesTest()
        {
            // Arrange
            IEnumerable<Note> expected = this.CreateNotesList();
 
            // Act
            IEnumerable<Note> actual = Repository.GetAllNotes();
 
            // Assert
            Assert.That(actual, Is.EquivalentTo(expected));
        }
 
        [TestMethod]
        public void AddNoteTest()
        {
            // Arrange
            int countBefore = this.Context.Notes.Count();
 
            // Act
            Repository.AddNote(new Note());
            int countAfter = this.Context.Notes.Count();
 
            // Assert
            Assert.That(countBefore + 1, Is.EqualTo(countAfter));
            Assert.That(this.Context.SavesChanged);
        }
 
        [TestMethod]
        public void EditNoteTest()
        {
            // Arrange
            Note noteToChange = new Note { NoteId = 2, Title = "Micky", Message = "Maus" };
 
            // Act
            Repository.EditNote(noteToChange);
            Note changedNote = (from n in this.Context.Notes
                                where n.NoteId == noteToChange.NoteId
                                select n).First();
 
            // Assert
            Assert.That(changedNote.Title, Is.EquivalentTo(noteToChange.Title));
            Assert.That(changedNote.Message, Is.EquivalentTo(noteToChange.Message));
            Assert.That(this.Context.SavesChanged);
        }
 
        [TestMethod]
        public void DeleteNoteShouldThrowExceptionTest()
        {
            // Arrange
            const int UnknownId = 9999;
 
            // Act
            TestDelegate deleteAction = () => Repository.DeleteNote(UnknownId);
 
            // Assert
            Assert.DoesNotThrow(deleteAction);
        }
 
        [TestMethod]
        [Ignore]
        public void GetNoteShouldThrowExceptionTest()
        {
            // Arrange
            const int UnknownId = 9999;
 
            // Act
            TestDelegate getAction = () => Repository.GetNote(UnknownId);
 
            // Assert
            Assert.Throws<InvalidOperationException>(getAction);
        }
 
        [TestMethod]
        public void GetNoteShouldNotCallSaveChangesTest()
        {
            // Arrange
            var expected = new Note
                {
                    NoteId = 3,
                    Title = "Unit",
                    Message = "Test",
                    Added = DateTime.Parse("2010-10-29", CultureInfo.InvariantCulture)
                };
 
            // Act
            var actual = Repository.GetNote(expected.NoteId);
 
            // Assert
            Assert.That(actual, Is.EqualTo(expected));
            Assert.That(!this.Context.SavesChanged);
        }
 
        /// <summary>
        /// Returns a collection for the MockObjectSet
        /// </summary>
        /// <returns>three Notes</returns>
        public override List<Note> CreateNotesList()
        {
            DateTime friday = DateTime.Parse("2010-10-29", CultureInfo.InvariantCulture);
 
            return new List<Note>
                {
                    new Note { NoteId = 1, Title = "Hello", Message = "World", Added = friday },
                    new Note { NoteId = 2, Title = "Foo", Message = "Bar", Added = friday },
                    new Note { NoteId = 3, Title = "Unit", Message = "Test", Added = friday }
                };
        }
    }
}

Step 5: Download the sample solution

All shown files can reviewed in a demo solution!


Download the extension here: 
ADO.NET Unit Testable Repository Generator

Tags | , , , , , ,

Kommentare

  1. Blogged: http://blog.johanneshoppe.de/2010/10/ado… – ADO.NET Mocking Context Generator – Extended – Walkthrough #dotnet #ccd #fb

  2. Jorge

    Hi,

    thank for your blog.I´m newbie with TDD and i´m trying to figure out how to use Unity on this project, could you give me an explanation or some code example?

    Thank you,

    Regards

  3. Hi, thanks for this, very useful indeed.

    To get my head around it I created a yUML diagram which I thought I’d share:

    http://yuml.me/512879d
    http://yuml.me/edit/512879d

    Hope I interpreted this correctly!

    • Hi, thanks for your interest to the T4 template! :-)

      The diagram is not entirely correct. I will try to find some time to make a second one tomorrow. (lot’s of work for me atm)

  4. You don’t mention how you built the UI, but I feel it’s pretty obvious from the sample project.

    I decided to re-implement your solution in MVC3 and so for the benefit of anyone else who wants to do the same…

    Copied the .mdb file over into the App_Data folder of an empty MVC3 project

    Also, I used NuGet Package Manager to add a new Library Package Reference to Microsoft.Patterns.Unity – nice and simple!

    [I also used NuGet to fetch me the NUnit package later on when I was adding the Unit Test project.]

    Added library reference to Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll – the Add Reference dialog in VS2010 found that no problem with out me needing to tell it the path.

    Followed your walkthrough (won’t repeat that here).

    Used ReShaprer to extract an interface IWebNoteRepository from WebNoteRepository

    My Controller was identical to yours.

    For MVC3 we don’t need the 3 Unity Helper classes you created in the Code/Unity we can make use of the IDependencyResolver interface by implementing it and registering this in the DependencyResolver static class
    (see http://blogs.microsoft.co.il/blogs/gilf/archive/2010/10/17/dependency-injection-in-mvc-3-was-made-easier.aspx )

    I have pasted the Unity Dependency Resolver code here:

    http://pastie.org/1430580

  5. Dan

    Was just experimenting with this and thought I’d point out a small oversight in the generation of the entity classes…. If one entity inherits from another, the generated CombineHashCodes() method needs to be declared „new“ to eliminate compiler warnings. The T4 file can be easily fixed to test for the entity having a base class and add the „new“ keyword if it does.

  6. Dan

    I was wondering if you knew what might be involved to update the templates in order to generate files for the Entity Framework CTP June 2011 release. When I switch to using this, the macros no longer function–I suspect there\’s a version impedence match somewhere but haven\’t looked into this much further. It would be a shame if extensive work were required to the macro files every time a new Entity Framework version comes out….

  7. JimRawr

    I don’t have access to „context“ when I try to make the repository?

  8. Escist

    How do I call ObjectContext.Refresh()? The autogenerated classes only have a method int SaveChanges();

Kommentar absenden


Projekt-
Verfügbarkeit:
ab 01.01.2017