Fehler bei Einsatz von Entity Framework 6 in Unit Tests

Eben ging mein Gated Checkin noch und jetzt nicht mehr. Der Fehler ist wie immer wenig hilfreich:

System.InvalidOperationException: The Entity Framework provider type ‚System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089‘ for the ‚System.Data.SqlClient‘ ADO.NET provider could not be loaded.

Die Lösung ist eigentlich recht leicht nur eben mit ein paar Tricksereien im Umfeld von Unit Tests auf dem Server verbunden.

Screencast

Informationen

Zu diesem Thema wurde bereits so einiges gepostet. Beispielsweise:

Das Problem ist nun, dass hier einige ganz nette Erklärungen und Lösungen gebracht werden, keine davon jedoch wirklich auf mein Problem anwendbar war. Aber eins nach dem anderen.

Schritte zum Problem

Ich habe eine bereits länger entwickeltes Solution. Hier habe ich ein Projekt, das per EF auf eine SQL Server Datenbank zugreift. Die Verweise werden per nuget verwaltet. Seit einiger Zeit folgt daraus nun, dass man immer 2 DLLs liefern muss, damit der Zugriff klappt:

  • EntityFramework.dll
  • EntityFramework.SqlServer.dll

Sowei so gut. Mein Unit-Test-Projekt sah also bis vor kurzem ca. so aus, dass auch dieses ein Nuget-Package auf EntityFramework hatte und alles war gut.

Dann kam ich auf die Idee, datengetriebene Tests zu integrieren. Hier ein  Beispiel:

[TestClass]
public class DateTimeExtensionsTest : DataDrivenTestBase
{
    #region methods

    /// <summary>
    /// Tests the correctness of the extension-method <see cref="DateTimeExtensions.BeginOfDay"/>.
    /// </summary>        
    [TestMethod]
    [DeploymentItem(@"Data Sources\DateTimeExtensionsTest_BeginOfDay.csv")]
    [DataSource(@"Microsoft.VisualStudio.TestTools.DataSource.CSV", 
        "DateTimeExtensionsTest_BeginOfDay.csv", 
        "DateTimeExtensionsTest_BeginOfDay#csv", 
        DataAccessMethod.Sequential)]
    public void BeginOfDayTest()
    {
        // Arange
        var input = Convert.ToDateTime(TestContext.DataRow[0]);
        var expected = Convert.ToDateTime(TestContext.DataRow[1]);
        // Act
        var result = input.BeginOfDay();
        // Assert
        Assert.AreEqual(expected, result);
    }

    #endregion
}

Also ein relativ simples kleines Ding. Nun habe ich fröhlich einen Rechtsklick rein gemacht und „Run Test“ gewählt. Das Ergebnis ist toll:

Abb. 1: Der Test klappt
Abb. 1: Der Test klappt

Weil ich mein Visual Studio ja nun bereits eine Weile kenne, wähle ich „Run All“ im Test Explorer aus. Ergebnis:
Abb. 2: Plötzlich schlagen Datentests fehl
Abb. 2: Plötzlich schlagen Datentests fehl

Also nur noch mal zur Verdeutlichung: An den Tests, die nun fehlschlagen habe ich nichts verändert! Kommentiere ich Listing 1 aus, geht wieder alles.

Lösung

Das Problem scheint zu sein, dass in dem Szenario, in dem ich einen datengetriebenen Test starte, das Framework die zweite Dll EntityFramework.SqlServer.dll „wegrationalisiert“. Ich habe keine Ahnung, warum, aber ich weiß, wie man das verhindern kann.

Ich lasse alle meine Tests schon immer von einer abstrakten Klasse „TestBase“ erben. Das hat den Vorteil, dass ich bestimmte Dinge zentral für alle Tests auf einen Schlag ändern kann. In Listing 1 ist die Basisklasse „DataDrivenTestBase“, die aber selbst auch wieder von TestBase erbt. In der TestBase mache ich nun nichts weiter, als:

public abstract class TestBase
{
    #region methods

    /// <summary>
    /// Is called by the testing framework before a test runs.
    /// </summary>
    [TestInitialize]
    public void Init()
    {
        // Handle an error in EF 6.
        var instance = System.Data.Entity.SqlServer.SqlProviderServices.Instance;
    }

    #endregion
}

Wiederhole ich nun mein „Run All“ im Test Explorer, funktioniert alles wieder einwandfrei!

Man sieht in Listing 2 recht gut, dass einfach nur ein Typ aus der Assembly System.Data.Entity.SqlServer benutzt wird. Allein das Benutzen scheint auszureichen, damit der Testrunner versteht, dass er diese Assembly nicht „weglassen“ darf.

Lösung, die nicht reicht

An einigen Stellen wird vorgeschlagen, folgendes zu nutzen:

var instance = typeof(System.Data.Entity.SqlServer.SqlProviderServices)

Testet man mit dieser Variante lokal ist alles fein. Bei mir kam aber der oben beschriebene Fehler immer noch, wenn der Build auf dem Build-Server durchgeführt werden musste. Man sollte also bis auf weiteres die Variante aus Listing 2 nehmen.

Fazit

In letzter Zeit häufen sich die kleinen aber lästigen Fehlerchen in den Toolings von MS. Ich habe den Eindruck, dass die getrennte und damit häufigere Verteilung per Nuget und VS Gallery nicht eben der Stabilität zu Gute gekommen sind. Ich habe mit diesem Kram gerade 2 Stunden Arbeit versenkt. Wenn sich das noch mehr häuft, dann frisst sowas meine Produktivitätsgewinne durch VS + ALM aber ganz schnell wieder auf :-(.

2 Antworten auf „Fehler bei Einsatz von Entity Framework 6 in Unit Tests“

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.