TDD – Log-In-Scenario mit einem Membership Provider

by Gregor Biswanger 31. Januar 2010 12:43

ASP.NET bietet mit dem Membership Provider eine eigene Lösung für eine Authentifizierung. Nun Verlangen die Anforderungen der späteren Web-Anwendung eine Authentifizierung, wobei dafür ein LogIn-Service selbst geschrieben werden muss, der mit dem Membership Provider kommuniziert. Wenn das Scenario nun mit TDD aufgebaut werden soll, muss automatisch ein Integrationstest folgen. Dazu muss ein Unit-Test auch auf den Membership-Provider zugreifen können. Dazu gibt es zwei Probleme. Zum ersten werden Unit-Tests nicht auf Web-Ebene ausgeführt und zum zweiten wird der Log-In-Service auch nicht in einem Web-Projekt erzeugt. Dieser wird in der Infrastructur-Schicht für alle anderen Schichten auch zur Verfügung gestellt.

Folgender Source-Code zeigt einen fertigen Unit-Test für einen Log-In-Service:

   1: using System.Collections.Specialized;
   2: using System.Web.Security;
   3: using MembershipExample.Infrastructure.Entities;
   4: using MembershipExample.Infrastructure.Services;
   5: using NUnit.Framework;
   6:  
   7: namespace MembershipUnitTestExample
   8: {
   9:     [TestFixture]
  10:     public class LogInServiceTest
  11:     {
  12:         private const string Username = "Testuser";
  13:         private const string Password = "Test123!";
  14:         private const string Email = "Testuser@noname.de";
  15:  
  16:         [SetUp]
  17:         public void SetUp()
  18:         {
  19:             MyMembershipProvider provider = new MyMembershipProvider();
  20:  
  21:             NameValueCollection config = new NameValueCollection();
  22:             config.Add("applicationName", "MyProvider");
  23:             config.Add("name", "MyMembershipProvider" );
  24:             config.Add("requiresQuestionAndAnswer", "false");
  25:             config.Add("connectionStringName", "MyProviderConnectionString");
  26:  
  27:             provider.Initialize(config["name"], config);
  28:  
  29:             MembershipCreateStatus createStatus;
  30:             provider.CreateUser(Username, Password, Email, null, null, true, null, out createStatus);
  31:         }
  32:  
  33:         [TearDown]
  34:         public void TearDown()
  35:         {
  36:             MyMembershipProvider provider = new MyMembershipProvider();
  37:  
  38:             NameValueCollection config = new NameValueCollection();
  39:             config.Add("applicationName", "MyProvider");
  40:             config.Add("name", "MyMembershipProvider");
  41:             config.Add("requiresQuestionAndAnswer", "false");
  42:             config.Add("connectionStringName", "MyProviderConnectionString");
  43:  
  44:             provider.Initialize(config["name"], config);
  45:  
  46:             provider.DeleteUser(Username, true);
  47:         }
  48:  
  49:         [Test]
  50:         public void User_is_authenticated()
  51:         {
  52:             // Arrange
  53:             UserAccount userAccount = new UserAccount();
  54:             userAccount.Name = Username;
  55:             userAccount.Password = Password;
  56:  
  57:             // Act
  58:             bool result = LogInService.IsUserAuthenticated(userAccount);
  59:  
  60:             // Assert
  61:             Assert.IsNotNull(result);
  62:             Assert.IsTrue(result);
  63:             Assert.IsTrue(Membership.ValidateUser(userAccount.Name, userAccount.Password));
  64:         }
  65:  
  66:         [Test]
  67:         public void User_is_not_authenticated()
  68:         {
  69:             // Arrange
  70:             UserAccount userAccount = new UserAccount();
  71:             userAccount.Name = "WrongName";
  72:             userAccount.Password = "NotRight!";
  73:  
  74:             // Act
  75:             bool result = LogInService.IsUserAuthenticated(userAccount);
  76:  
  77:             // Assert
  78:             Assert.IsNotNull(result);
  79:             Assert.IsFalse(result);
  80:             Assert.IsFalse(Membership.ValidateUser(userAccount.Name, userAccount.Password));
  81:         }
  82:     }
  83: }

 

Mit SetUp und TearDown wird ein Testbenutzer im Membership-Provider vor jeder Test-Methode abgelegt und anschließend wieder entfernt. Um auf den Membership-Provider zugreifen zu können müssen folgende Assemblies referenziert werden: „system.web“ und „system.configuration“. Damit überhaupt eine Instanz auf den Provider stattfinden kann, wird noch eine App.config-Datei mit einem Connection-String und der Zuweisung eines selbst geschriebenen Providers benötigt.

Folgender Source-Code zeigt die App.Config-Datei:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:   <connectionStrings>
   4:     <add name="MyProviderConnectionString" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename='C:\Users\Gregor.Biswanger\Documents\Visual Studio 2008\Projects\MembershipUnitTestExample\MembershipUnitTestExample\App_Data\ASPNETDB.MDF';Integrated Security=True;User Instance=True"/>
   5:   </connectionStrings>
   6:  
   7:   <system.web>
   8:  
   9:     <membership defaultProvider="MyMembershipProvider">
  10:       <providers>
  11:         <remove name="AspNetSqlMembershipProvider"/>
  12:  
  13:         <add applicationName="MyProvider" 
  14:              requiresQuestionAndAnswer="false"
  15:              requiresUniqueEmail="false" 
  16:              minRequiredNonalphanumericCharacters="0"
  17:              enablePasswordReset="true" 
  18:              passwordFormat="Hashed" 
  19:              connectionStringName="MyProviderConnectionString"
  20:              name="MyMembershipProvider" 
  21:              type="MembershipExample.Infrastructure.Services.MyMembershipProvider,MembershipExample.Infrastructure" />
  22:  
  23:       </providers>
  24:     </membership>
  25:   </system.web>
  26: </configuration>

 

Nun ist der Test fertig geschrieben und die Klassen LogInService und MyMembershipProvider müssen in eine eigene Schicht: „MembershipExample.Infrastructure“. Die LogInService-Klasse bekommt eine Statische Methode „public static bool IsUserAuthentivatet(UserAccount userAccount)“. Die MyMembershipProvider-Klasse leitet nur von der Klasse SqlMembershipProvider ab. Das war es auch schon. Nun wird noch im Web-Projekt eine Membership Datenbank (Sql Server) erzeugt. Dazu wird im Solution-Explorer die ASP.NET Configuration aufgerufen.

ASP.NET-Configuration-Wizard

Abb.1. – Die ASP.NET Configuration aufrufen.

Anschließend öffnet sich die Konfiguration im Web-Browser. Hierbei wird links vom Bildschirm auf den Link „Authentifizierungstyp auswählen“.

Websiteverwaltungs-Tool-Authentifizierungstyp

Abb.2. – Authentifizierungstyp auswählen.

Beim folgenden Bildschirm wird „Aus dem Internet“ ausgewählt. Da es später eine Formularbasierte Authentifizierung wird.

Websiteverwaltungs-Tool-Aus_dem_Internet

Abb.3. – „Aus dem Internet“ auswählen

Nach dem Fertigstellen wurde dem Web-Projekt nun eine SQL Server Datenbankdatei mit der Membership Datenbank im App_Data-Verzeichnis erzeugt. Um dies einsehen zu können, muss im Solution-Explorer auf das Icon „Show All Files“ geklickt werden. Danach wird der ASP.NET Webserver im Try-Menü beendet, damit nun die Datenbank in das Testprojekt mit demselben Verzeichnis App_Data kopiert werden kann.

App_Data_DB_kopieren

Abb.4. – Membership Datenbank kopieren

Wichtig hierbei ist, dass nun der Pfad, beim ConnectionString in der App.Config korrekt eingestellt ist. Nun kann der Test gestartet werden, der jetzt jedoch erst noch Fehlschlagen muss. Dazu dient die Exception-Klasse NotImplementedException sehr gut. Wenn dies ohne weitere Probleme funktioniert hat, kann nun der eigentliche Source-Code implementiert werden.

Implementierung des Log-In-Services:

   1: using System.Web.Security;
   2: using MembershipExample.Infrastructure.Entities;
   3:  
   4: namespace MembershipExample.Infrastructure.Services
   5: {
   6:     public class LogInService
   7:     {
   8:         public static bool IsUserAuthenticated( UserAccount userAccount )
   9:         {
  10:             return Membership.ValidateUser(userAccount.Name, userAccount.Password);
  11:         }
  12:     }
  13: }

 

Implementierung von MyMembershipProvider:

   1: using System.Web.Security;
   2: namespace MembershipExample.Infrastructure.Services
   3: {
   4:     public class MyMembershipProvider : SqlMembershipProvider
   5:     {}
   6: }

 

Fazit

Bei diesem Test handelt es sich quasi um einen Test des Membership Providers. Dennoch muss auch ein Integrationstest für den Log-In-Service geschrieben werden, wobei dieser Test dafür sehr gut geeignet ist. Das Projekt kann hier runtergeladen werden, wobei hier in der App.Config der Pfad im Connection-String angepasst werden muss.

unit-test-succeeded

Source-Code Download:

MembershipUnitTestExample.zip (575,94 kb)



Wenn ihnen der Artikel gefallen hat oder er für sie hilfreich war, bitten "kicken" sie ihn.
kick it on dotnet-kicks.de

Kommentare

Powered by BlogEngine.NET 1.4.5.0
Theme by Extensive SEO

Über den Autor

Gregor Biswanger

Microsoft MVP für Client App Dev
XING

Gregor Biswanger (Microsoft MVP für Client App Dev) ist freier Consultant, Trainer, Autor und Speaker.


Seine Schwerpunkte liegen im Bereich der .NET-Architektur, agilen Prozessen und XAML. Er veröffentlichte vor kurzem seine DVD´s mit Video-Trainings zum Thema „Meine erste Windows 8 App“, „Windows Store Apps mit XAML und C#“ und „WPF 4.5 und Silverlight 5“ bei Addison-Wesley von video2brain.


Biswanger ist auch im Auftrag von Intel GmbH als Technologieberater für die Intel Developer Zone aktiv und ist Leader bei der Ingolstädter .NET Developers Group (INdotNET). 

 

Video über mich:
http://www.youtube.com/watch?v=mx_6SiiLxjk


Basta! 2011 Speaker

CLIPer

MCTS
Windows SharePoint Services 3.0 – Application Development (MCTS)