Aracılığıyla paylaş


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

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 (C#)

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 oluşturuyoruz. 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, varsayılan ASP.NET MVC görünüm ana sayfasını ve basamaklı stil sayfasını değiştirerek uygulamanın görünümünü iyileştireceğiz.

  • Yineleme #3 - Form doğrulaması ekleyin. Üçüncü yinelemede temel form doğrulaması ekliyoruz. Gerekli form alanlarını tamamlamadan kişilerin form göndermesini engelleriz. Ayrıca e-posta adreslerini ve telefon numaralarını da 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 desteği 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, birim testlerini test temelli geliştirme adı verilen bir uygulama tasarım felsefesinin parçası olarak kullanıyoruz. Test temelli geliştirme alıştırması yaptığınızda, önce testleri yazar ve ardından testlere kod yazarsınız.

Daha kesin olarak, test temelli geliştirmeyi uygularken kod oluştururken tamamladığınız üç 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 kodunu henüz 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 minimum kod miktarını yazmaya odaklanmanız gerekir.

Son olarak, yeterince kod 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 deseni 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 korkusuz bir şekilde 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 yalnızca belirli bir testi geçmek için yeterli kod yazmaya odaklandığınızdan, hiçbir zaman kullanmayacağınız çok büyük miktarlardaki kodu yazmanız 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 kullanıcı perspektifinden sürekli 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 pencere dışına çıkan ilk şeydir. Öte yandan, test temelli geliştirme yaparken birim testleri yazma konusunda daha verimli olabilirsiniz çü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 Kodla 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 göre yazacağız.

Test Edilenler

Önceki yinelemede de belirttiğimiz gibi, genellikle veri erişim mantığı veya görüntüleme mantığı için birim testleri yazmazsınız. Veritabanına erişim görece 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ünüm mantığı için birim testleri yazmazsınız. Test çok hızlı bir şekilde tekrar tekrar yürütülmedikç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 hemen şu soruyu ortaya çıkar: İ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şlevinin 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ı var olan bir kişi grubunu silebilir.
  4. Kullanıcı, yeni kişi oluştururken bir kişi grubu 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 bir müşteri tarafından tamamen anlaşılabilir olduğuna dikkat edin. Teknik uygulama ayrıntılarından bahsedilmiyor.

Uygulamanızı oluşturma işlemi sırasında 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.cs 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.cs

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Web.Mvc;
using ContactManager.Models;

namespace ContactManager.Tests.Controllers
{
    [TestClass]
    public class GroupControllerTest
    {

        [TestMethod]
        public void Index()
        {
            // Arrange
            var controller = new GroupController();

            // Act
            var result = (ViewResult)controller.Index();
        
            // Assert
            Assert.IsInstanceOfType(result.ViewData.Model, typeof(IEnumerable));
        }
    }
}

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.cs

using System.Collections.Generic;
using System.Web.Mvc;
using ContactManager.Models;

namespace ContactManager.Controllers
{
    public class GroupController : Controller
    {
        public ActionResult Index()
        {
            var groups = new List();
            return View(groups);
        }

    }
}

Listeleme 3 - Models\Group.cs

namespace ContactManager.Models
{
    public class Group
    {
    }
}

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.cs

[TestMethod]
public void Create()
{
    // Arrange
    var controller = new GroupController();

    // Act
    var groupToCreate = new Group();
    controller.Create(groupToCreate);

    // Assert
    var result = (ViewResult)controller.Index();
    var groups = (IEnumerable<Group>)result.ViewData.Model;
    CollectionAssert.Contains(groups.ToList(), groupToCreate);
}

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.cs

using System.Collections.Generic;
using System.Web.Mvc;
using ContactManager.Models;
using System.Collections;

namespace ContactManager.Controllers
{
    public class GroupController : Controller
    {
        private IList<Group> _groups = new List<Group>();

        public ActionResult Index()
        {
            return View(_groups);
        }

        public ActionResult Create(Group groupToCreate)
        {
            _groups.Add(groupToCreate);
            return RedirectToAction("Index");
        }
    }
}

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.cs

[TestMethod]
public void CreateRequiredName()
{
    // Arrange
    var controller = new GroupController();

    // Act
    var groupToCreate = new Group();
    groupToCreate.Name = String.Empty;
    var result = (ViewResult)controller.Create(groupToCreate);

    // Assert
    var error = result.ViewData.ModelState["Name"].Errors[0];
    Assert.AreEqual("Name is required.", error.ErrorMessage);
}

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.cs

namespace ContactManager.Models
{
    public class Group
    {
        public string Name { get; set; }
    }
}

Listeleme 8 - Controllers\GroupController.cs

public ActionResult Create(Group groupToCreate)
{
    // Validation logic
    if (groupToCreate.Name.Trim().Length == 0)
    {
        ModelState.AddModelError("Name", "Name is required.");
        return View("Create");
    }
    
    // Database logic
    _groups.Add(groupToCreate);
    return RedirectToAction("Index");
}

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.cs

using System.Web.Mvc;
using ContactManager.Models;

namespace ContactManager.Controllers
{
    public class GroupController : Controller
    {

        private IContactManagerService _service;

        public GroupController()
        {
            _service = new ContactManagerService(new ModelStateWrapper(this.ModelState));
        }

        public GroupController(IContactManagerService service)
        {
            _service = service;
        }

        public ActionResult Index()
        {
            return View(_service.ListGroups());
        }

        public ActionResult Create(Group groupToCreate)
        {
            if (_service.CreateGroup(groupToCreate))
                return RedirectToAction("Index");
            return View("Create");
        }
    }
}

Liste 10 - Controllers\ContactManagerService.cs

public bool ValidateGroup(Group groupToValidate)
{
    if (groupToValidate.Name.Trim().Length == 0)
       _validationDictionary.AddError("Name", "Name is required.");
    return _validationDictionary.IsValid;
}

public bool CreateGroup(Group groupToCreate)
{
    // Validation logic
    if (!ValidateGroup(groupToCreate))
        return false;

    // Database logic
    try
    {
        _repository.CreateGroup(groupToCreate);
    }
    catch
    {
        return false;
    }
    return true;
}

public IEnumerable<Group> ListGroups()
{
    return _repository.ListGroups();
}

Listeleme 11 - Controllers\FakeContactManagerRepository.cs

using System;
using System.Collections.Generic;
using ContactManager.Models;

namespace ContactManager.Tests.Models
{
    public class FakeContactManagerRepository : IContactManagerRepository
    {
        private IList<Group> _groups = new List<Group>(); 
        
        #region IContactManagerRepository Members

        // Group methods

        public Group CreateGroup(Group groupToCreate)
        {
            _groups.Add(groupToCreate);
            return groupToCreate;
        }

        public IEnumerable<Group> ListGroups()
        {
            return _groups;
        }

        // Contact methods
        
        public Contact CreateContact(Contact contactToCreate)
        {
            throw new NotImplementedException();
        }

        public void DeleteContact(Contact contactToDelete)
        {
            throw new NotImplementedException();
        }

        public Contact EditContact(Contact contactToEdit)
        {
            throw new NotImplementedException();
        }

        public Contact GetContact(int id)
        {
            throw new NotImplementedException();
        }

        public IEnumerable<Contact> ListContacts()
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}

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 Group CreateGroup(Group groupToCreate)
{
    throw new NotImplementedException();
}

public IEnumerable<Group> ListGroups()
{
    throw new NotImplementedException();
}

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.cs

using System.Collections.Generic;
using System.Web.Mvc;
using ContactManager.Controllers;
using ContactManager.Models;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections;
using System.Linq;
using System;
using ContactManager.Tests.Models;

namespace ContactManager.Tests.Controllers
{
    [TestClass]
    public class GroupControllerTest
    {
        private IContactManagerRepository _repository;
        private ModelStateDictionary _modelState;
        private IContactManagerService _service;

        [TestInitialize]
        public void Initialize()
        {
            _repository = new FakeContactManagerRepository();
            _modelState = new ModelStateDictionary();
            _service = new ContactManagerService(new ModelStateWrapper(_modelState), _repository);

        }

        [TestMethod]
        public void Index()
        {
            // Arrange
            var controller = new GroupController(_service);

            // Act
            var result = (ViewResult)controller.Index();
        
            // Assert
            Assert.IsInstanceOfType(result.ViewData.Model, typeof(IEnumerable));
        }

        [TestMethod]
        public void Create()
        {
            // Arrange
            var controller = new GroupController(_service);

            // Act
            var groupToCreate = new Group();
            groupToCreate.Name = "Business";
            controller.Create(groupToCreate);

            // Assert
            var result = (ViewResult)controller.Index();
            var groups = (IEnumerable)result.ViewData.Model;
            CollectionAssert.Contains(groups.ToList(), groupToCreate);
        }

        [TestMethod]
        public void CreateRequiredName()
        {
            // Arrange
            var controller = new GroupController(_service);

            // Act
            var groupToCreate = new Group();
            groupToCreate.Name = String.Empty;
            var result = (ViewResult)controller.Create(groupToCreate);

            // Assert
            var error = _modelState["Name"].Errors[0];
            Assert.AreEqual("Name is required.", error.ErrorMessage);
        }
    
    }
}

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.cs

using System.Collections.Generic;

namespace ContactManager.Models
{
    public interface IContactManagerRepository
    {
        // Contact methods
        Contact CreateContact(int groupId, Contact contactToCreate);
        void DeleteContact(Contact contactToDelete);
        Contact EditContact(int groupId, Contact contactToEdit);
        Contact GetContact(int id);

        // Group methods
        Group CreateGroup(Group groupToCreate);
        IEnumerable<Group> ListGroups();
        Group GetGroup(int groupId);
        Group GetFirstGroup();
        void DeleteGroup(Group groupToDelete);
    }
}

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 IEnumerable<Group> ListGroups()
{
    throw new NotImplementedException();
}

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.cs

using System.Collections.Generic;
using System.Linq;
using System;

namespace ContactManager.Models
{
    public class EntityContactManagerRepository : ContactManager.Models.IContactManagerRepository
    {
        private ContactManagerDBEntities _entities = new ContactManagerDBEntities();

        // Contact methods

        public Contact GetContact(int id)
        {
            return (from c in _entities.ContactSet.Include("Group")
                    where c.Id == id
                    select c).FirstOrDefault();
        }

        public Contact CreateContact(int groupId, Contact contactToCreate)
        {
            // Associate group with contact
            contactToCreate.Group = GetGroup(groupId);

            // Save new contact
            _entities.AddToContactSet(contactToCreate);
            _entities.SaveChanges();
            return contactToCreate;
        }

        public Contact EditContact(int groupId, Contact contactToEdit)
        {
            // Get original contact
            var originalContact = GetContact(contactToEdit.Id);
            
            // Update with new group
            originalContact.Group = GetGroup(groupId);
            
            // Save changes
            _entities.ApplyPropertyChanges(originalContact.EntityKey.EntitySetName, contactToEdit);
            _entities.SaveChanges();
            return contactToEdit;
        }

        public void DeleteContact(Contact contactToDelete)
        {
            var originalContact = GetContact(contactToDelete.Id);
            _entities.DeleteObject(originalContact);
            _entities.SaveChanges();
        }

        public Group CreateGroup(Group groupToCreate)
        {
            _entities.AddToGroupSet(groupToCreate);
            _entities.SaveChanges();
            return groupToCreate;
        }

        // Group Methods

        public IEnumerable<Group> ListGroups()
        {
            return _entities.GroupSet.ToList();
        }

        public Group GetFirstGroup()
        {
            return _entities.GroupSet.Include("Contacts").FirstOrDefault();
        }

        public Group GetGroup(int id)
        {
            return (from g in _entities.GroupSet.Include("Contacts")
                       where g.Id == id
                       select g).FirstOrDefault();
        }

        public void DeleteGroup(Group groupToDelete)
        {
            var originalGroup = GetGroup(groupToDelete.Id);
            _entities.DeleteObject(originalGroup);
            _entities.SaveChanges();

        }

    }
}

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.