Wednesday, 25 June 2014

Using Entity Framework in integration tests

Entity Framework code first includes an interesting feature called database initializers. When I first encountered this feature I wondered if it would be possible to use a database initializer to drop and recreate a database as part of a suite of integration tests. It might prove very useful if we were able to create a set of repeatable and atomic tests around, for example, a data access layer. Of course it turns out that this is possible.

At the time of writing Entity Framework is in version 6.1.1.

 

What is a database initializer?

A database initializer is an implementation of the IDatabaseInitializer<TContext> interface and is used by Entity Framework to setup the database when the context is used for the first time. This could involve dropping and recreating the entire database, or just updating the schema if the model has changed. MSDN describes the interface as follows:

“An implementation of this interface is used to initialize the underlying database when an instance of a DbContext derived class is used for the first time. This initialization can conditionally create the database and/or seed it with data.” [1]

There are several implementations available out of the box:

  • DropCreateDatabaseIfModelChanges<TContext> – will DELETE, recreate, and optionally re-seed the database only if the model has changed since the database was created.
  • DropCreateDatabaseAlways<TContext> - will always recreate and optionally re-seed the database the first time that a context is used in the app domain. To seed the database, create a derived class and override the Seed method.
  • CreateDatabaseIfNotExists<TContext> - will recreate and optionally re-seed the database only if the database does not exist. To seed the database, create a derived class and override the Seed method.

In our scenario we always want to reinitialize the database before each test. This will give us a repeatable baseline at the start of each test case. Also, we probably want to be able to insert some known test data. The DropCreateDatabaseAlways<TContext> class looks like it does exactly what we want: it always recreates the schema and can optionally re-seed the database.

 

Creating an initializer derived from DropCreateDatabaseAlways<TContext>

Creating our custom initializer turns out to be very simple:

namespace Andy.French.Repository.Entity.Framework.Tests
{
    using System.Data.Entity;
    using System.Data.Entity.Migrations;

    using Andy.French.Repository.Entity.Framework.Tests.Domain;

    public class TestInitializer : DropCreateDatabaseAlways<TestContext>
    {
        protected override void Seed(TestContext context)
        {
            context.Customers.AddOrUpdate(
                                c => c.Id,
                                new Customer { Name = "Customer 1" },
                                new Customer { Name = "Customer 2" });

            base.Seed(context);
        }
    }
}

In this example I’m seeding the database with a couple of customers. Naturally you would seed the database according to your needs using your domain classes.

 

Invoking the initializer in our test suite

I use NUnit for tests - and have done so for a very long time – so the example below is based on NUnit.

For true atomic repeatable tests you should drop and recreate the database before each and every test case but for this example I have chosen to do so once for each test fixture - a class that contains tests. To do that I have created a class that’s marked with the SetUpFixture attribute. NUnit will pick this up and will run the method marked with the SetUp attribute for each test fixture and before any of the tests it contains are run.

namespace Andy.French.Repository.Entity.Framework.Tests
{
    using System.Data.Entity;
    using NUnit.Framework;

    [SetUpFixture]
    public class SetUpFixture
    {
        public SetUpFixture()
        {
        }

        [SetUp]
        public void SetUp()
        {
            Database.SetInitializer(new TestInitializer());
            
            var context = new TestContext();
            context.Database.Initialize(true);
        }
    }
}

That’s all here is to it! On line 16 we call the Database.SetInitializer method so Entity Framework will use our custom initializer. Remember, the custom initializer extends DropCreateDatabaseAlways<TestContext> so the initializer is set for the test context type.

On lines 18 and 19 we simply create a context and call context.Database.Initialize(true) which causes the database to be dropped, recreated and re-seeded. MSDN describes the Initialize method in the following terms:

“Runs the the registered IDatabaseInitializer<TContext> on this context. If "force" is set to true, then the initializer is run regardless of whether or not it has been run before. This can be useful if a database is deleted while an app is running and needs to be reinitialized. If "force" is set to false, then the initializer is only run if it has not already been run for this context, model, and connection in this app domain. This method is typically used when it is necessary to ensure that the database has been created and seeded before starting some operation where doing so lazily will cause issues, such as when the operation is part of a transaction.” [2]

 

Running a test

With everything in place we can now run some tests. Here’s an example where we are testing a customer repository:

namespace Andy.French.Repository.Entity.Framework.Tests
{
    using System.Linq;
    using NUnit.Framework;

    [TestFixture]
    public class CustomerRepositoryTest
    {
        private CustomerRepository _repository;

        [SetUp]
        public void SetUp()
        {
            var context = new TestContext();
            _repository = new CustomerRepository(context);
        }

        [Test]
        public void FindAll_WhenCalled_FindsAllInstances()
        {
            // Arrange

            // Act
            var result = _repository.FindAll();

            // Assert
            Assert.That(result.Count(), Is.EqualTo(2));
        }
    }
}

 

A note on configuration

You will need to add an app.config file to your project. The config file will have to tell Entity Framework which database to use. It might look something like this:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false"/>
  </configSections>
  
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0"/>
      </parameters>
    </defaultConnectionFactory>
    
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer"/>
    </providers>
  </entityFramework>
  
  <startup>
     <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/>
  </startup>
</configuration>

Note that we are using a SQL Server Express local database (v11.0). In my case the result is I get a couple of database files (.mdf and .ldf) named after the context used by the database initializer in my user directory.

 

image

 

References

[1] DatabaseInitializer<TContext> Interface (MSDN)

[2] Database.Initialize Method (MSDN)

Saturday, 21 June 2014

Integrating StyleCop with TeamCity

Having used CruiseControl.Net for some time I thought it was time to try something new: TeamCity from JetBrains. I’m a bit fussy about code quality so one thing I like my integration builds to do is run StyleCop and fail the build if violations are found.

Create an MSBuild file

After some research I tracked down some basic guidance on StackOverflow and adapted it to my needs [1].

I created an MSBuild file that could be referenced from a umber of TeamCity build configurations. This build file invokes StyleCop, counts the violations and fails the build if StyleCop violations are encountered. I saved the build file to a shared location where it could be used from different builds. Here’s the basic script:

 

<Project DefaultTargets="RunStyleCop" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
	<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
    <Import Project="$(ProgramFiles)\MSBuild\StyleCop\v4.7\StyleCop.targets" />
	<UsingTask TaskName="XmlRead" AssemblyFile="C:\MSBuild\lib\MSBuild.Community.Tasks.dll" />

	<Target Name="RunStyleCop">
        <CreateItem Include="$(teamcity_build_checkoutDir)\**\*.cs">
            <Output TaskParameter="Include" ItemName="StyleCopFiles" />
        </CreateItem>

        <StyleCopTask ProjectFullPath="$(MSBuildProjectFile)"
                      SourceFiles="@(StyleCopFiles)"
                      ForceFullAnalysis="true"
                      TreatErrorsAsWarnings="true"
                      OutputFile="StyleCopReport.xml"
                      CacheResults="true"
                      AdditionalAddinPaths="$(ProgramFiles)\StyleCop 4.7\Andy.French.StyleCop.Rules.dll"
                      OverrideSettingsFile="$(teamcity_build_checkoutDir)\Settings.StyleCop" />
                      
        <XmlRead XPath="count(//Violation)" XmlFileName="StyleCopReport.xml">
            <Output TaskParameter="Value" PropertyName="StyleCopViolations" />
        </XmlRead>

        <TeamCitySetStatus Status="$(AllPassed)" Text="StyleCop violations: $(StyleCopViolations)" />

        <Error Condition="$(StyleCopViolations) > 0" Text="There were $(StyleCopViolations) StyleCop violations." />
	</Target>
</Project>

 

On line 2 we import the StyleCop.targets from the StyleCop installation directory. This makes the StyleCopTask available on line 10. If you examine this file you’ll find it references the StyleCop.dll in the StyleCop installation directory. The StyleCopTask is actually in that DLL.

On line 3 we import the MSBuild.Community.Tasks.dll. This is an open source project that adds some useful MSBuild tasks including the XmlRead task on line 19 (see [2] below).

You may have to hop on over to the project GitHub site to grab a release [3]. I downloaded the Zip file and extracted the DLLs that I wanted, putting them in a shared location (C:\MSBuild\lib\ in this case).

The RunStyleCop target does all the work. On line 6 we grab all the C# files in the solution. Note that we are using a TeamCity variable here: teamcity.build.checkoutDir. NB: Don’t forget you have to replace all instances of “.” with “_” if you are using MSBuild.

 

“Make sure to replace "." with "_" when using properties in MSBuild scripts; e.g. use teamcity_dotnet_nunitlauncher_msbuild_task instead of teamcity.dotnet.nunitlauncher.msbuild.task” [4]

 

Now I have some custom StyleCop rules and I like to disable a couple of the default rules. To activate my custom StyleCop rules DLL I had to specify the path to it using the AdditionalAddinPaths attribute on line 16. I also include a Settings.StyleCop file with overridden settings with each solution so on line 17 I set the OverrideSettingsFile attribute to point to that file.

The XmlRead task on line 19 reads the output from StyleCop and makes the result available in a property called StyleCopViolations. This is used on line 23 to report the number of violations to TeamCity using the TeamCitySetStatus task on line 23. Then, on line 25, we throw an error – failing the build – if there are any errors.

TeamCity configuration

It’s quite straight forward then in TeamCity. You need to add a Build Step to your Build Configuration. I set the path to the shared build file created above and specified the target:

 

image

 

Note I have set the path to the shared build file and included the target to run. Here’s an example of a failed build, first in the Projects overview where our status message is displayed:

 

image

 

And in the build results page where the Error message can be seen:

 

image

 

There’s more work to do, for example getting the violations to display better but for now I can get at them via the build log tab.

References

Wednesday, 18 June 2014

Download SQL Server Express

Only a few days ago we were moaning in the office about how complicated it was to dowload the correct version of SQL Server Express. Well, it seems we were not alone.

Scott Hanselman has come to the rescue with an awesome blog post that provides easy links to the various SQL Server Express binaries. Here's Scott's short link to his blog post:

http://downloadsqlserverexpress.com

One for the bookmark list. Thanks Scott!