Aracılığıyla paylaş


6. Yineleme – Test odaklı geliştirme kullanma (VB)

Microsoft tarafından

Kodu İndir

Bu altıncı yinelemede, önce birim testleri yazıp birim testlerine kod yazarak uygulamamıza yeni işlevler ekleyeceğiz. Bu yinelemede kişi grupları ekleyeceğiz.

MVC Uygulaması ASP.NET Kişi Yönetimi Oluşturma (VB)

Bu öğretici serisinde, baştan sona bir Kişi Yönetimi uygulamasının tamamını oluşturuyoruz. Contact Manager uygulaması kişi listesi için kişi bilgilerini (adlar, telefon numaraları ve e-posta adresleri) depolamanıza olanak tanır.

Uygulamayı birden çok yineleme üzerinden derleyeceğiz. Her yinelemede uygulamayı aşamalı olarak iyileştiriyoruz. Bu çoklu yineleme yaklaşımının amacı, her değişikliğin nedenini anlamanıza olanak tanımaktır.

  • Yineleme #1 - Uygulamayı oluşturun. İlk yinelemede, Contact Manager'ı mümkün olan en basit şekilde oluştururuz. Temel veritabanı işlemleri için destek ekliyoruz: Oluşturma, Okuma, Güncelleştirme ve Silme (CRUD).

  • Yineleme #2 - Uygulamanın güzel görünmesini sağlayın. Bu yinelemede, MVC görünümü ana sayfasını ve basamaklı stil sayfasını varsayılan ASP.NET değiştirerek uygulamanın görünümünü iyileştireceğiz.

  • 3. Yineleme - Form doğrulaması ekleyin. Üçüncü yinelemede temel form doğrulamasını ekleyeceğiz. Gerekli form alanlarını tamamlamadan kişilerin form göndermesini engelleriz. Ayrıca e-posta adreslerini ve telefon numaralarını doğrularız.

  • Yineleme #4 - Uygulamayı gevşek bir şekilde birleştirin. Bu dördüncü yinelemede, Contact Manager uygulamasının bakımını ve değiştirilmesini kolaylaştırmak için çeşitli yazılım tasarım desenlerinden yararlanıyoruz. Örneğin, uygulamamızı Depo desenini ve Bağımlılık Ekleme düzenini kullanacak şekilde yeniden düzenleyeceğiz.

  • Yineleme #5 - Birim testleri oluşturun. Beşinci yinelemede, birim testleri ekleyerek uygulamamızın bakımını ve değiştirilmesini kolaylaştırıyoruz. Veri modeli sınıflarımızla dalga geçiyoruz ve denetleyicilerimiz ve doğrulama mantığımız için birim testleri oluşturuyoruz.

  • Yineleme #6 - Test temelli geliştirmeyi kullanın. Bu altıncı yinelemede, önce birim testleri yazıp birim testlerine kod yazarak uygulamamıza yeni işlevler ekleyeceğiz. Bu yinelemede kişi grupları ekleyeceğiz.

  • Yineleme #7 - Ajax işlevselliğini ekleyin. Yedinci yinelemede Ajax için destek ekleyerek uygulamamızın yanıt hızını ve performansını iyileştiriyoruz.

Bu Yineleme

Contact Manager uygulamasının önceki yinelemesinde, kodumuz için bir güvenlik ağı sağlamak üzere birim testleri oluşturduk. Birim testlerini oluşturmanın amacı, kodumuzu değiştirmek için daha dayanıklı hale getirmekti. Birim testleri gerçekleştirdiğimizde kodumuzda herhangi bir değişiklik yapabilir ve mevcut işlevselliği bozup bozmadığımıza hemen karar verebiliriz.

Bu yinelemede, birim testlerini tamamen farklı bir amaç için kullanırız. Bu yinelemede, test temelli geliştirme adı verilen uygulama tasarım felsefesinin bir parçası olarak birim testlerini kullanırız. Test temelli geliştirme alıştırması yaptığınızda, önce testleri yazar ve ardından testlere karşı kod yazarsınız.

Daha kesin olarak, test temelli geliştirmeyi uygularken kod oluştururken tamamlamanız gereken üç adım vardır (Kırmızı/ Yeşil/Yeniden Düzenleme):

  1. Başarısız olan birim testi yazma (Kırmızı)
  2. Birim testini geçen kod yazma (Yeşil)
  3. Kodunuzu yeniden düzenleme (Yeniden düzenleme)

İlk olarak birim testini yazarsınız. Birim testi, kodunuzun nasıl davranmasını beklediğinizi ifade etmelidir. Birim testini ilk oluşturduğunuzda birim testi başarısız olmalıdır. Testi karşılayan herhangi bir uygulama kodu yazmadığınız için test başarısız olmalıdır.

Ardından, birim testinin geçmesi için yeterli kodu yazacaksınız. Amaç kodu mümkün olan en tembel, en eğimli ve en hızlı şekilde yazmaktır. Uygulamanızın mimarisini düşünmekle zaman kaybetmemelisiniz. Bunun yerine, birim testi tarafından ifade edilen amacı karşılamak için gereken en az miktarda kodu yazmaya odaklanmanız gerekir.

Son olarak, yeterli kodu yazdıktan sonra geri adım atabilir ve uygulamanızın genel mimarisini göz önünde bulundurabilirsiniz. Bu adımda, kodunuzun daha sürdürülebilir olması için, depo düzeni gibi yazılım tasarım desenlerinden yararlanarak kodunuzu yeniden yazar (yeniden düzenlersiniz). Kodunuz birim testlerinin kapsamına eklendiğinden, bu adımda kodunuzu korkusuzca yeniden yazabilirsiniz.

Test temelli geliştirmenin sonucunda elde edilen birçok avantaj vardır. İlk olarak, test temelli geliştirme sizi gerçekten yazılması gereken koda odaklanmaya zorlar. Sürekli olarak belirli bir testi geçmek için yeterli kodu yazmaya odaklandığınızdan, hiçbir zaman kullanmayacağınız çok miktarda kod yazmanız ve bu tür işlemlerin içine girmeniz engellenir.

İkincisi, "önce test et" tasarım metodolojisi, kodunuzun nasıl kullanılacağı açısından kod yazmaya zorlar. Başka bir deyişle, test temelli geliştirme yaparken, testlerinizi sürekli olarak kullanıcı perspektifinden yazarsınız. Bu nedenle test temelli geliştirme, daha temiz ve daha anlaşılır API'lere neden olabilir.

Son olarak, test temelli geliştirme sizi bir uygulama yazma işleminin normal bir parçası olarak birim testleri yazmaya zorlar. Proje son tarihi yaklaştıkça, test genellikle pencereden çıkan ilk şeydir. Öte yandan, test temelli geliştirme yaparken birim testleri yazma konusunda daha verimli olma olasılığınız daha yüksektir çünkü test temelli geliştirme, birim testlerini uygulama oluşturma sürecinin merkezi haline getirir.

Not

Test temelli geliştirme hakkında daha fazla bilgi edinmek için Michael Feathers'un Eski Kod ile Etkili Çalışma kitabını okumanızı öneririm.

Bu yinelemede, Contact Manager uygulamamıza yeni bir özellik ekleyeceğiz. Kişi Grupları için destek ekliyoruz. Kişilerinizi İş ve Arkadaş grupları gibi kategoriler halinde düzenlemek için Kişi Gruplarını kullanabilirsiniz.

Test temelli geliştirme sürecini izleyerek bu yeni işlevi uygulamamıza ekleyeceğiz. Önce birim testlerimizi yazacağız ve tüm kodumuzu bu testlere karşı yazacağız.

Test Edilenler

Önceki yinelemede ele aldığımız gibi, genellikle veri erişim mantığı için birim testleri yazmaz veya mantığı görüntülemezsiniz. Veritabanına erişim nispeten yavaş bir işlem olduğundan, veri erişim mantığı için birim testleri yazmazsınız. Görünüme erişmek görece yavaş bir işlem olan bir web sunucusu oluşturmayı gerektirdiği için görüntüleme mantığı için birim testleri yazmazsınız. Test çok hızlı bir şekilde tekrar tekrar yürütülemedikçe birim testi yazmamalısınız

Test temelli geliştirme birim testlerine göre oluşturulduğundan, başlangıçta denetleyici ve iş mantığı yazmaya odaklanıyoruz. Veritabanına veya görünümlere dokunmaktan kaçınıyoruz. Bu öğreticinin sonuna kadar veritabanını değiştirmeyeceğiz veya görünümlerimizi oluşturmayacağız. Test edilebileceklerle başlıyoruz.

Kullanıcı Hikayeleri Oluşturma

Test temelli geliştirme alıştırması yaparken, her zaman bir test yazarak başlarsınız. Bu soru hemen şu soruyu doğurur: İlk olarak hangi testi yazabileceğinize nasıl karar verirsiniz? Bu soruyu yanıtlamak için bir dizi kullanıcı hikayesi yazmanız gerekir.

Kullanıcı hikayesi, yazılım gereksiniminin çok kısa (genellikle bir cümle) açıklamasıdır. Kullanıcı açısından yazılmış bir gereksinimin teknik olmayan bir açıklaması olmalıdır.

Yeni Kişi Grubu işlevselliğinin gerektirdiği özellikleri açıklayan kullanıcı hikayeleri kümesi aşağıdadır:

  1. Kullanıcı kişi gruplarının listesini görüntüleyebilir.
  2. Kullanıcı yeni bir kişi grubu oluşturabilir.
  3. Kullanıcı mevcut bir kişi grubunu silebilir.
  4. Kullanıcı, yeni kişi oluştururken kişi grubunu seçebilir.
  5. Kullanıcı, mevcut bir kişiyi düzenlerken kişi grubunu seçebilir.
  6. Kişi gruplarının listesi Dizin görünümünde görüntülenir.
  7. Kullanıcı bir kişi grubuna tıkladığında, eşleşen kişilerin listesi görüntülenir.

Bu kullanıcı hikayeleri listesinin müşteri tarafından tamamen anlaşılabilir olduğuna dikkat edin. Teknik uygulama ayrıntılarından bahsedilmemiştir.

Uygulamanızı oluşturma sürecindeyken, kullanıcı hikayeleri kümesi daha iyi hale gelebilir. Bir kullanıcı hikayesini birden çok hikayeye (gereksinimler) bölebilirsiniz. Örneğin, yeni kişi grubu oluşturma işleminin doğrulamayı içermesi gerektiğine karar vekleyebilirsiniz. Kişi grubunu ad olmadan göndermek doğrulama hatası döndürmelidir.

Kullanıcı hikayelerinin listesini oluşturduktan sonra, ilk birim testinizi yazmaya hazırsınız. Kişi gruplarının listesini görüntülemek için bir birim testi oluşturarak başlayacağız.

Kişi Gruplarını Listeleme

İlk kullanıcı hikayemiz, bir kullanıcının kişi gruplarının listesini görüntüleyebilmesi gerektiğidir. Bu hikayeyi bir testle ifade etmeliyiz.

ContactManager.Tests projesinde Denetleyiciler klasörüne sağ tıklayıp Ekle, Yeni Test'i ve Birim Testi şablonunu seçerek yeni bir birim testi oluşturun (bkz. Şekil 1). Yeni birim testini GroupControllerTest.vb olarak adlandırın ve Tamam düğmesine tıklayın.

GroupControllerTest birim testi ekleme

Şekil 01: GroupControllerTest birim testi ekleme (Tam boyutlu görüntüyü görüntülemek için tıklayın)

İlk birim testimiz Liste 1'de yer alır. Bu test, Grup denetleyicisinin Index() yönteminin bir Grup kümesi döndürdüğünü doğrular. Test, bir Grup koleksiyonunun görünüm verilerinde döndürüldüğünü doğrular.

Listeleme 1 - Controllers\GroupControllerTest.vb

Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports System.Web.Mvc

<TestClass()> _
Public Class GroupControllerTest

    <TestMethod()> _
    Public Sub Index()
        ' Arrange
        Dim controller = New GroupController()

        ' Act
        Dim result = CType(controller.Index(), ViewResult)

        ' Assert
        Assert.IsInstanceOfType(result.ViewData.Model, GetType(IEnumerable(Of Group)))
    End Sub
End Class

Visual Studio'da Listeleme 1'e kodu ilk kez yazdığınızda, çok fazla kırmızı dalgalı çizgi alırsınız. GroupController veya Group sınıflarını oluşturmadık.

Bu noktada, ilk birim testimizi yürütemememiz için uygulamamızı bile oluşturamıyoruz. Bu iyi oldu. Bu, başarısız bir test olarak sayılır. Bu nedenle artık uygulama kodu yazmaya başlama iznimiz var. Testimizi yürütmek için yeterli kod yazmamız gerekiyor.

Listeleme 2'deki Grup denetleyicisi sınıfı, birim testini geçirmek için gereken en düşük kodu içerir. Index() eylemi, statik olarak kodlanmış bir Grup listesi döndürür (Grup sınıfı Listeleme 3'te tanımlanır).

Listeleme 2 - Controllers\GroupController.vb

Public Class GroupController
    Inherits System.Web.Mvc.Controller

    Function Index() As ActionResult
        Dim groups = new List(Of Group)
        Return View(groups)
    End Function

End Class

Listeleme 3 - Models\Group.vb

Public Class Group

End Class

GroupController ve Group sınıflarını projemize ekledikten sonra ilk birim testimiz başarıyla tamamlanır (bkz. Şekil 2). Testi geçmek için gereken minimum çalışmayı yaptık. Kutlama zamanı.

Başarı!

Şekil 02: Başarı! (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Kişi Grupları Oluşturma

Artık ikinci kullanıcı hikayesine geçebiliriz. Yeni kişi grupları oluşturabilmemiz gerekiyor. Bu amacı bir testle ifade etmeliyiz.

Listeleme 4'teki test, Create() yöntemini yeni bir Grupla çağırmanın Grubu Index() yöntemi tarafından döndürülen Gruplar listesine eklediğini doğrular. Başka bir deyişle, yeni bir grup oluşturursam, yeni Grubu Index() yöntemi tarafından döndürülen Gruplar listesinden geri alabilmeliyim.

Listeleme 4 - Controllers\GroupControllerTest.vb

<TestMethod> _
Public Sub Create()
    ' Arrange
    Dim controller = New GroupController()

    ' Act
    Dim groupToCreate = New Group()
    controller.Create(groupToCreate)

    ' Assert
    Dim result = CType(controller.Index(), ViewResult)
    Dim groups = CType(result.ViewData.Model, IEnumerable(Of Group))
    CollectionAssert.Contains(groups.ToList(), groupToCreate)
End Sub

Listeleme 4'teki test, yeni bir kişi Grubu ile Grup denetleyicisi Create() yöntemini çağırır. Daha sonra test, Grup denetleyicisi Index() yöntemini çağırmanın verileri görüntülemede yeni Grubu döndürdüğünü doğrular.

Liste 5'teki değiştirilen Grup denetleyicisi, yeni testi geçmek için gereken en az değişikliği içerir.

Listeleme 5 - Controllers\GroupController.vb

Public Class GroupController
Inherits Controller

Private _groups As IList(Of Group) = New List(Of Group)()

Public Function Index() As ActionResult
    Return View(_groups)
End Function

Public Function Create(ByVal groupToCreate As Group) As ActionResult
    _groups.Add(groupToCreate)
    Return RedirectToAction("Index")

End Function
End Class

Liste 5'teki Grup denetleyicisinin yeni bir Create() eylemi var. Bu eylem, Gruplar koleksiyonuna bir Grup ekler. Index() eyleminin, Gruplar koleksiyonunun içeriğini döndürecek şekilde değiştirildiğine dikkat edin.

Bir kez daha birim testini geçmek için gereken minimum çalışma miktarını gerçekleştirdik. Grup denetleyicisinde bu değişiklikleri yaptıktan sonra tüm birim testlerimiz geçer.

Doğrulama Ekleme

Bu gereksinim, kullanıcı hikayesinde açıkça belirtilmedi. Ancak, bir Grubun bir ada sahip olmasını zorunlu kılabilir. Aksi takdirde, kişileri Gruplar halinde düzenlemek çok yararlı olmaz.

6. liste, bu amacı ifade eden yeni bir test içerir. Bu test, bir ad sağlamadan Grup oluşturma girişiminin model durumunda doğrulama hata iletisiyle sonuçlandığını doğrular.

Listeleme 6 - Controllers\GroupControllerTest.vb

<TestMethod> _
Public Sub CreateRequiredName()
    ' Arrange
    Dim controller = New GroupController()

    ' Act
    Dim groupToCreate As New Group()
    groupToCreate.Name = String.Empty
    Dim result = CType(controller.Create(groupToCreate), ViewResult)

    ' Assert
    Dim [error] = result.ViewData.ModelState("Name").Errors(0)
    Assert.AreEqual("Name is required.", [error].ErrorMessage)
End Sub

Bu testi karşılamak için Grup sınıfımıza bir Ad özelliği eklememiz gerekir (bkz. Listeleme 7). Ayrıca, Grup denetleyicisinin Create() eylemine küçük bir doğrulama mantığı eklememiz gerekir (bkz. Listeleme 8).

Listeleme 7 - Models\Group.vb

Public Class Group

    Private _name As String

    Public Property Name() As String
    Get
        Return _name
    End Get
    Set(ByVal value As String)
        _name = value
    End Set
End Property

End Class

Liste 8 - Controllers\GroupController.vb

Public Function Create(ByVal groupToCreate As Group) As ActionResult
    ' Validation logic
    If groupToCreate.Name.Trim().Length = 0 Then
    ModelState.AddModelError("Name", "Name is required.")
    Return View("Create")
    End If

    ' Database logic
    _groups.Add(groupToCreate)
    Return RedirectToAction("Index")
End Function

Grup denetleyicisi Create() eyleminin artık hem doğrulama hem de veritabanı mantığı içerdiğine dikkat edin. Şu anda, Grup denetleyicisi tarafından kullanılan veritabanı bellek içi bir koleksiyondan başka bir şeyden ibarettir.

Yeniden Düzenleme Zamanı

Kırmızı/Yeşil/Yeniden Düzenleme'nin üçüncü adımı Yeniden Düzenleme bölümüdür. Bu noktada kodumuzdan geri dönmemiz ve tasarımını geliştirmek için uygulamamızı nasıl yeniden düzenleyebileceğimizi düşünmemiz gerekir. Yeniden düzenleme aşaması, yazılım tasarım ilkelerini ve desenlerini uygulamanın en iyi yolu hakkında çok düşündüğümüz aşamadır.

Kodun tasarımını geliştirmek için seçtiğimiz herhangi bir şekilde kodumuzu değiştirebiliriz. Mevcut işlevselliği bozmamızı engelleyen birim testlerinden oluşan bir güvenlik ağımız var.

Şu anda, Grup denetleyicimiz iyi bir yazılım tasarımı açısından bir karmaşa. Grup denetleyicisi, karmaşık bir doğrulama ve veri erişim kodu karmaşası içerir. Tek Sorumluluk İlkesi'ni ihlal etmekten kaçınmak için bu endişeleri farklı sınıflara ayırmamız gerekir.

Yeniden düzenlenmiş Grup denetleyicisi sınıfımız Listeleme 9'da yer alır. Denetleyici ContactManager hizmet katmanını kullanacak şekilde değiştirildi. Bu, Kişi denetleyicisinde kullandığımız hizmet katmanıyla aynıdır.

Liste 10, grupları doğrulamayı, listelemeyi ve oluşturmayı desteklemek için ContactManager hizmet katmanına eklenen yeni yöntemleri içerir. IContactManagerService arabirimi yeni yöntemleri içerecek şekilde güncelleştirildi.

Liste 11, IContactManagerRepository arabirimini uygulayan yeni bir FakeContactManagerRepository sınıfı içerir. IContactManagerRepository arabirimini de uygulayan EntityContactManagerRepository sınıfından farklı olarak, yeni FakeContactManagerRepository sınıfımız veritabanıyla iletişim kurmaz. FakeContactManagerRepository sınıfı, veritabanı için ara sunucu olarak bellek içi koleksiyonu kullanır. Bu sınıfı birim testlerimizde sahte depo katmanı olarak kullanacağız.

Listeleme 9 - Controllers\GroupController.vb

Public Class GroupController
Inherits Controller

Private _service As IContactManagerService

Public Sub New()
    _service = New ContactManagerService(New ModelStateWrapper(Me.ModelState))

End Sub

Public Sub New(ByVal service As IContactManagerService)
    _service = service
End Sub

Public Function Index() As ActionResult
    Return View(_service.ListGroups())
End Function


Public Function Create(ByVal groupToCreate As Group) As ActionResult
    If _service.CreateGroup(groupToCreate) Then
        Return RedirectToAction("Index")
    End If
    Return View("Create")
End Function

End Class

Liste 10 - Controllers\ContactManagerService.vb

Public Function ValidateGroup(ByVal groupToValidate As Group) As Boolean
If groupToValidate.Name.Trim().Length = 0 Then
    _validationDictionary.AddError("Name", "Name is required.")
End If
Return _validationDictionary.IsValid
End Function

Public Function CreateGroup(ByVal groupToCreate As Group) As Boolean Implements IContactManagerService.CreateGroup
    ' Validation logic
    If Not ValidateGroup(groupToCreate) Then
        Return False
    End If

    ' Database logic
    Try
        _repository.CreateGroup(groupToCreate)
    Catch
        Return False
    End Try
    Return True
End Function

Public Function ListGroups() As IEnumerable(Of Group) Implements IContactManagerService.ListGroups
    Return _repository.ListGroups()
End Function

Listeleme 11 - Controllers\FakeContactManagerRepository.vb

Public Class FakeContactManagerRepository
Implements IContactManagerRepository

Private _groups As IList(Of Group) = New List(Of Group)()

#Region "IContactManagerRepository Members"

' Group methods

Public Function CreateGroup(ByVal groupToCreate As Group) As Group Implements IContactManagerRepository.CreateGroup
    _groups.Add(groupToCreate)
    Return groupToCreate
End Function

Public Function ListGroups() As IEnumerable(Of Group) Implements IContactManagerRepository.ListGroups
    Return _groups
End Function

' Contact methods

Public Function CreateContact(ByVal contactToCreate As Contact) As Contact Implements IContactManagerRepository.CreateContact
    Throw New NotImplementedException()
End Function

Public Sub DeleteContact(ByVal contactToDelete As Contact) Implements IContactManagerRepository.DeleteContact
    Throw New NotImplementedException()
End Sub

Public Function EditContact(ByVal contactToEdit As Contact) As Contact Implements IContactManagerRepository.EditContact
    Throw New NotImplementedException()
End Function

Public Function GetContact(ByVal id As Integer) As Contact Implements IContactManagerRepository.GetContact
    Throw New NotImplementedException()
End Function

Public Function ListContacts() As IEnumerable(Of Contact) Implements IContactManagerRepository.ListContacts
    Throw New NotImplementedException()
End Function

#End Region
End Class

IContactManagerRepository arabirimini değiştirmek için EntityContactManagerRepository sınıfında CreateGroup() ve ListGroups() yöntemlerini uygulamak için kullanılması gerekir. Bunu yapmak için en tembel ve en hızlı yol aşağıdakine benzer saplama yöntemleri eklemektir:

Public Function CreateGroup(groupToCreate As Group) As Group Implements IContactManagerRepository.CreateGroup

    throw New NotImplementedException()

End Function 

Public Function ListGroups() As IEnumerable(Of Group) Implements IContactManagerRepository.ListGroups

    throw New NotImplementedException()

End Function

Son olarak, uygulamamızın tasarımında yapılan bu değişiklikler, birim testlerimizde bazı değişiklikler yapmamızı gerektirir. Şimdi birim testlerini gerçekleştirirken FakeContactManagerRepository'yi kullanmamız gerekiyor. Güncelleştirilmiş GroupControllerTest sınıfı Listeleme 12'de yer alır.

Listeleme 12 - Controllers\GroupControllerTest.vb

Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports System.Web.Mvc

<TestClass()> _
Public Class GroupControllerTest

    Private _repository As IContactManagerRepository
    Private _modelState As ModelStateDictionary
    Private _service As IContactManagerService

    <TestInitialize()> _
    Public Sub Initialize()
        _repository = New FakeContactManagerRepository()
        _modelState = New ModelStateDictionary()
        _service = New ContactManagerService(New ModelStateWrapper(_modelState), _repository)
    End Sub

    <TestMethod()> _
    Public Sub Index()
        ' Arrange
        Dim controller = New GroupController(_service)

        ' Act
        Dim result = CType(controller.Index(), ViewResult)

        ' Assert
        Assert.IsInstanceOfType(result.ViewData.Model, GetType(IEnumerable(Of Group)))
    End Sub

    <TestMethod()> _
    Public Sub Create()
        ' Arrange
        Dim controller = New GroupController(_service)

        ' Act
        Dim groupToCreate = New Group()
        groupToCreate.Name = "Business"
        controller.Create(groupToCreate)

        ' Assert
        Dim result = CType(controller.Index(), ViewResult)
        Dim groups = CType(result.ViewData.Model, IEnumerable(Of Group))
        CollectionAssert.Contains(groups.ToList(), groupToCreate)
    End Sub

    <TestMethod()> _
    Public Sub CreateRequiredName()
        ' Arrange
        Dim controller = New GroupController(_service)

        ' Act
        Dim groupToCreate = New Group()
        groupToCreate.Name = String.Empty
        Dim result = CType(controller.Create(groupToCreate), ViewResult)

        ' Assert
        Dim nameError = _modelState("Name").Errors(0)
        Assert.AreEqual("Name is required.", nameError.ErrorMessage)
    End Sub

End Class

Tüm bu değişiklikleri yaptıktan sonra, bir kez daha tüm birim testlerimiz geçer. Kırmızı/Yeşil/Yeniden Düzenleme döngüsünün tamamını tamamladık. İlk iki kullanıcı hikayesini uyguladık. Artık kullanıcı hikayelerinde ifade edilen gereksinimler için destekleyici birim testlerimiz var. Kullanıcı hikayelerinin geri kalanını uygulamak için aynı Kırmızı/Yeşil/Yeniden Düzenleme döngüsünün yinelenmesi gerekir.

Veritabanımızı Değiştirme

Ne yazık ki, birim testlerimiz tarafından ifade edilen tüm gereksinimleri karşılamış olsak da, işimiz yapılmamaktadır. Yine de veritabanımızı değiştirmemiz gerekiyor.

Yeni bir Grup veritabanı tablosu oluşturmamız gerekiyor. Şu adımları izleyin:

  1. Sunucu Gezgini penceresinde Tablolar klasörüne sağ tıklayın ve Yeni Tablo Ekle menü seçeneğini belirleyin.
  2. Tablo Tasarım Aracı aşağıda açıklanan iki sütunu girin.
  3. Kimlik sütununu birincil anahtar ve Kimlik sütunu olarak işaretleyin.
  4. Disketin simgesine tıklayarak yeni tabloyu Gruplar adıyla kaydedin.

Sütun Adı Veri Türü Null'lara İzin Ver
Id int Yanlış
Name nvarchar(50) Yanlış

Ardından, Kişiler tablosundaki tüm verileri silmemiz gerekir (aksi takdirde, Kişiler ve Gruplar tabloları arasında ilişki oluşturamayız). Şu adımları izleyin:

  1. Kişiler tablosuna sağ tıklayın ve Tablo Verilerini Göster menü seçeneğini belirleyin.
  2. Tüm satırları silin.

Ardından, Gruplar veritabanı tablosu ile mevcut Kişiler veritabanı tablosu arasında bir ilişki tanımlamamız gerekir. Şu adımları izleyin:

  1. Sunucu Gezgini penceresinde Kişiler tablosuna çift tıklayarak Tablo Tasarım Aracı açın.
  2. Kişiler tablosuna GroupId adlı yeni bir tamsayı sütunu ekleyin.
  3. Yabancı Anahtar İlişkileri iletişim kutusunu açmak için İlişki düğmesine tıklayın (bkz. Şekil 3).
  4. Ekle düğmesine tıklayın.
  5. Tablo ve Sütun Belirtimi düğmesinin yanında görünen üç nokta düğmesine tıklayın.
  6. Tablolar ve Sütunlar iletişim kutusunda birincil anahtar tablosu olarak Gruplar'ı ve birincil anahtar sütunu olarak Kimlik'i seçin. Yabancı anahtar tablosu olarak Kişiler'i ve yabancı anahtar sütunu olarak GroupId'yi seçin (bkz. Şekil 4). Tamam düğmesine tıklayın.
  7. INSERT ve UPDATE Belirtimi'nin altında, Kuralı Sil için Art Arda Ekle değerini seçin.
  8. Yabancı Anahtar İlişkileri iletişim kutusunu kapatmak için Kapat düğmesine tıklayın.
  9. Kişiler tablosundaki değişiklikleri kaydetmek için Kaydet düğmesine tıklayın.

Veritabanı tablosu ilişkisi oluşturma

Şekil 03: Veritabanı tablosu ilişkisi oluşturma (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Tablo ilişkilerini belirtme

Şekil 04: Tablo ilişkilerini belirtme (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Veri Modelimizi Güncelleştirme

Ardından, yeni veritabanı tablosunu temsil etmek için veri modelimizi güncelleştirmemiz gerekiyor. Şu adımları izleyin:

  1. Entity Tasarım Aracı açmak için Models klasöründeki ContactManagerModel.edmx dosyasına çift tıklayın.
  2. Tasarım Aracı yüzeye sağ tıklayın ve Modeli Veritabanından Güncelleştir menü seçeneğini belirleyin.
  3. Güncelleştirme Sihirbazı'nda Gruplar tablosunu seçin ve Son düğmesine tıklayın (bkz. Şekil 5).
  4. Gruplar varlığına sağ tıklayın ve Yeniden Adlandır menü seçeneğini belirleyin. Gruplar varlığının adını Grup (tekil) olarak değiştirin.
  5. Kişi varlığının en altında görüntülenen Gruplar gezinti özelliğine sağ tıklayın. Gruplar gezinti özelliğinin adını Grup (tekil) olarak değiştirin.

Veritabanından Entity Framework modelini güncelleştirme

Şekil 05: Veritabanından bir Entity Framework modelini güncelleştirme (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Bu adımları tamamladıktan sonra veri modeliniz hem Kişiler hem de Gruplar tablolarını temsil eder. Varlık Tasarım Aracı her iki varlığı da göstermelidir (bkz. Şekil 6).

Grup ve Kişi'yi görüntüleyen Varlık Tasarım Aracı

Şekil 06: Grup ve Kişi'yi görüntüleyen varlık Tasarım Aracı(Tam boyutlu görüntüyü görüntülemek için tıklayın)

Depo Sınıflarımızı Oluşturma

Ardından, depo sınıfımızı uygulamamız gerekir. Bu yineleme sırasında, birim testlerimizi karşılamak için kod yazarken IContactManagerRepository arabirimine birkaç yeni yöntem ekledik. IContactManagerRepository arabiriminin son sürümü Listeleme 14'te yer alır.

Listeleme 14 - Models\IContactManagerRepository.vb

Public Interface IContactManagerRepository
' Contact methods
Function CreateContact(ByVal groupId As Integer, ByVal contactToCreate As Contact) As Contact
Sub DeleteContact(ByVal contactToDelete As Contact)
Function EditContact(ByVal groupId As Integer, ByVal contactToEdit As Contact) As Contact
Function GetContact(ByVal id As Integer) As Contact

' Group methods
Function CreateGroup(ByVal groupToCreate As Group) As Group
Function ListGroups() As IEnumerable(Of Group)
Function GetGroup(ByVal groupId As Integer) As Group
Function GetFirstGroup() As Group
Sub DeleteGroup(ByVal groupToDelete As Group)

End Interface

Gerçek EntityContactManagerRepository sınıfımızda kişi gruplarıyla çalışmayla ilgili yöntemlerin hiçbirini uygulamadık. Şu anda EntityContactManagerRepository sınıfı, IContactManagerRepository arabiriminde listelenen kişi grubu yöntemlerinin her biri için saptama yöntemlerine sahiptir. Örneğin, ListGroups() yöntemi şu anda şöyle görünür:

Public Function ListGroups() As IEnumerable(Of Group) Implements IContactManagerRepository.ListGroups

    throw New NotImplementedException()

End Function

Saptama yöntemleri, uygulamamızı derlememizi ve birim testlerini geçirmemizi sağladı. Ancak şimdi bu yöntemleri uygulama zamanı geldi. EntityContactManagerRepository sınıfının son sürümü Listeleme 13'te yer alır.

Listeleme 13 - Models\EntityContactManagerRepository.vb

Public Class EntityContactManagerRepository
Implements IContactManagerRepository

Private _entities As New ContactManagerDBEntities()

' Contact methods

Public Function GetContact(ByVal id As Integer) As Contact Implements IContactManagerRepository.GetContact
    Return (From c In _entities.ContactSet.Include("Group") _
            Where c.Id = id _
            Select c).FirstOrDefault()
End Function

Public Function CreateContact(ByVal groupId As Integer, ByVal contactToCreate As Contact) As Contact Implements IContactManagerRepository.CreateContact
    ' Associate group with contact
    contactToCreate.Group = GetGroup(groupId)

    ' Save new contact
    _entities.AddToContactSet(contactToCreate)
    _entities.SaveChanges()
    Return contactToCreate
End Function

Public Function EditContact(ByVal groupId As Integer, ByVal contactToEdit As Contact) As Contact Implements IContactManagerRepository.EditContact
    ' Get original contact
    Dim originalContact = GetContact(contactToEdit.Id)

    ' Update with new group
    originalContact.Group = GetGroup(groupId)

    ' Save changes
    _entities.ApplyPropertyChanges(originalContact.EntityKey.EntitySetName, contactToEdit)
    _entities.SaveChanges()
    Return contactToEdit
End Function

Public Sub DeleteContact(ByVal contactToDelete As Contact) Implements IContactManagerRepository.DeleteContact 
    Dim originalContact = GetContact(contactToDelete.Id)
    _entities.DeleteObject(originalContact)
    _entities.SaveChanges()
End Sub

    ' Group methods

Public Function CreateGroup(ByVal groupToCreate As Group) As Group Implements IContactManagerRepository.CreateGroup 
    _entities.AddToGroupSet(groupToCreate)
    _entities.SaveChanges()
    Return groupToCreate
End Function

Public Function ListGroups() As IEnumerable(Of Group) Implements IContactManagerRepository.ListGroups
    Return _entities.GroupSet.ToList()
End Function

Public Function GetFirstGroup() As Group Implements IContactManagerRepository.GetFirstGroup
    Return _entities.GroupSet.Include("Contacts").FirstOrDefault()
End Function

Public Function GetGroup(ByVal id As Integer) As Group Implements IContactManagerRepository.GetGroup
    Return (From g In _entities.GroupSet.Include("Contacts") _
            Where g.Id = id _
            Select g).FirstOrDefault()
End Function

Public Sub DeleteGroup(ByVal groupToDelete As Group) Implements IContactManagerRepository.DeleteGroup
    Dim originalGroup = GetGroup(groupToDelete.Id)
    _entities.DeleteObject(originalGroup)
    _entities.SaveChanges()
End Sub

End Class

Görünümleri Oluşturma

Varsayılan ASP.NET görünüm altyapısını kullandığınızda MVC uygulamasını ASP.NET. Bu nedenle, belirli bir birim testine yanıt olarak görünümler oluşturmazsınız. Ancak, bir uygulama görünümler olmadan işe yaramaz olacağından, Contact Manager uygulamasındaki görünümleri oluşturmadan ve değiştirmeden bu yinelemeyi tamamlayamayız.

Kişi gruplarını yönetmek için aşağıdaki yeni görünümleri oluşturmamız gerekiyor (bkz. Şekil 7):

  • Views\Group\Index.aspx - Kişi gruplarının listesini görüntüler
  • Views\Group\Delete.aspx - Kişi grubunu silmek için onay formunu görüntüler

Grup Dizini görünümü

Şekil 07: Grup Dizini görünümü (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Aşağıdaki mevcut görünümleri kişi gruplarını içerecek şekilde değiştirmemiz gerekir:

  • Views\Home\Create.aspx
  • Views\Home\Edit.aspx
  • Views\Home\Index.aspx

Bu öğreticiye eşlik eden Visual Studio uygulamasına bakarak değiştirilmiş görünümleri görebilirsiniz. Örneğin, Şekil 8'de Kişi Dizini görünümü gösterilmektedir.

Kişi Dizini görünümü

Şekil 08: Kişi Dizini görünümü (Tam boyutlu görüntüyü görüntülemek için tıklayın)

Özet

Bu yinelemede, test temelli geliştirme uygulaması tasarım metodolojisini izleyerek Contact Manager uygulamamıza yeni işlevler ekledik. İlk olarak bir kullanıcı hikayeleri kümesi oluşturduk. Kullanıcı hikayeleri tarafından ifade edilen gereksinimlere karşılık gelen bir dizi birim testi oluşturduk. Son olarak, birim testlerinin ifade ettiği gereksinimleri karşılamak için yeterli kodu yazdık.

Birim testlerinin ifade ettiği gereksinimleri karşılamak için yeterli kod yazmayı tamamladıktan sonra veritabanımızı ve görünümlerimizi güncelleştirdik. Veritabanımıza yeni bir Gruplar tablosu ekledik ve Entity Framework Veri Modelimizi güncelleştirdik. Ayrıca bir görünüm kümesi oluşturup değiştirdik.

Sonraki yinelemede (son yinelemede) Ajax'ın avantajlarından yararlanmak için uygulamamızı yeniden yazacağız. Ajax'ın avantajlarından yararlanarak Contact Manager uygulamasının yanıt hızını ve performansını geliştireceğiz.