Bagikan melalui


Iterasi #6 – Gunakan pengembangan berbasis pengujian (VB)

oleh Microsoft

Unduh Kode

Dalam iterasi keenam ini, kami menambahkan fungsionalitas baru ke aplikasi kami dengan menulis pengujian unit terlebih dahulu dan menulis kode terhadap pengujian unit. Dalam perulangan ini, kami menambahkan grup kontak.

Membangun Manajemen Kontak ASP.NET Aplikasi MVC (VB)

Dalam rangkaian tutorial ini, kami membangun seluruh aplikasi Manajemen Kontak dari awal hingga akhir. Aplikasi Contact Manager memungkinkan Anda menyimpan informasi kontak - nama, nomor telepon, dan alamat email - untuk daftar orang.

Kami membangun aplikasi melalui beberapa iterasi. Dengan setiap iterasi, kami secara bertahap meningkatkan aplikasi. Tujuan dari pendekatan perulangan ganda ini adalah untuk memungkinkan Anda memahami alasan setiap perubahan.

  • Iterasi #1 - Buat aplikasi. Dalam iterasi pertama, kami membuat Contact Manager dengan cara yang paling sederhana. Kami menambahkan dukungan untuk operasi database dasar: Buat, Baca, Perbarui, dan Hapus (CRUD).

  • Iterasi #2 - Membuat aplikasi terlihat bagus. Dalam perulangan ini, kami meningkatkan tampilan aplikasi dengan memodifikasi halaman master tampilan ASP.NET MVC default dan lembar gaya bertingkat.

  • Iterasi #3 - Tambahkan validasi formulir. Dalam iterasi ketiga, kami menambahkan validasi formulir dasar. Kami mencegah orang mengirimkan formulir tanpa melengkapi bidang formulir yang diperlukan. Kami juga memvalidasi alamat email dan nomor telepon.

  • Iterasi #4 - Buat aplikasi digabungkan secara longgar. Dalam iterasi keempat ini, kami memanfaatkan beberapa pola desain perangkat lunak untuk mempermudah pemeliharaan dan modifikasi aplikasi Contact Manager. Misalnya, kami merefaktor aplikasi kami untuk menggunakan pola Repositori dan pola Injeksi Dependensi.

  • Iterasi #5 - Membuat pengujian unit. Dalam iterasi kelima, kami membuat aplikasi kami lebih mudah dirawat dan dimodifikasi dengan menambahkan pengujian unit. Kami meniru kelas model data kami dan membangun pengujian unit untuk pengontrol dan logika validasi kami.

  • Iterasi #6 - Gunakan pengembangan berbasis pengujian. Dalam iterasi keenam ini, kami menambahkan fungsionalitas baru ke aplikasi kami dengan menulis pengujian unit terlebih dahulu dan menulis kode terhadap pengujian unit. Dalam perulangan ini, kami menambahkan grup kontak.

  • Iterasi #7 - Tambahkan fungsionalitas Ajax. Dalam iterasi ketujuh, kami meningkatkan responsivitas dan performa aplikasi kami dengan menambahkan dukungan untuk Ajax.

Perulangan Ini

Dalam iterasi sebelumnya dari aplikasi Contact Manager, kami membuat pengujian unit untuk menyediakan jaring pengaman untuk kode kami. Motivasi untuk membuat pengujian unit adalah untuk membuat kode kami lebih tangguh untuk diubah. Dengan pengujian unit di tempat, kita dapat dengan senang hati membuat perubahan pada kode kita dan segera tahu apakah kita telah merusak fungsionalitas yang ada.

Dalam iterasi ini, kami menggunakan pengujian unit untuk tujuan yang sama sekali berbeda. Dalam perulangan ini, kami menggunakan pengujian unit sebagai bagian dari filosofi desain aplikasi yang disebut pengembangan berbasis pengujian. Saat Anda mempraktikkan pengembangan berbasis pengujian, Anda menulis pengujian terlebih dahulu lalu menulis kode terhadap pengujian.

Lebih tepatnya, saat mempraktikkan pengembangan berbasis pengujian, ada tiga langkah yang Anda selesaikan saat membuat kode (Merah/ Hijau/Refaktor):

  1. Menulis pengujian unit yang gagal (Merah)
  2. Menulis kode yang lulus uji unit (Hijau)
  3. Refaktor kode Anda (Refaktor)

Pertama, Anda menulis pengujian unit. Pengujian unit harus mengekspresikan niat Anda tentang bagaimana Anda mengharapkan kode Anda berulah. Ketika Anda pertama kali membuat pengujian unit, pengujian unit harus gagal. Pengujian harus gagal karena Anda belum menulis kode aplikasi apa pun yang memenuhi pengujian.

Selanjutnya, Anda menulis kode yang cukup agar pengujian unit lulus. Tujuannya adalah untuk menulis kode dengan cara yang paling lazim, paling miring, dan tercepat. Anda tidak boleh membuang-buang waktu untuk memikirkan arsitektur aplikasi Anda. Sebagai gantinya, Anda harus fokus pada penulisan jumlah kode minimal yang diperlukan untuk memenuhi niat yang dinyatakan oleh pengujian unit.

Akhirnya, setelah Anda menulis kode yang cukup, Anda dapat mundur dan mempertimbangkan arsitektur keseluruhan aplikasi Anda. Dalam langkah ini, Anda menulis ulang (refaktor) kode Anda dengan memanfaatkan pola desain perangkat lunak -- seperti pola repositori -- sehingga kode Anda lebih dapat dipertahankan. Anda dapat dengan takut menulis ulang kode Anda dalam langkah ini karena kode Anda dicakup oleh pengujian unit.

Ada banyak manfaat yang dihasilkan dari mempraktikkan pengembangan berbasis pengujian. Pertama, pengembangan berbasis pengujian memaksa Anda untuk fokus pada kode yang sebenarnya perlu ditulis. Karena Anda terus-menerus fokus pada hanya menulis kode yang cukup untuk lulus tes tertentu, Anda dicegah berkeliaran ke gulma dan menulis sejumlah besar kode yang tidak akan pernah Anda gunakan.

Kedua, metodologi desain "uji pertama" memaksa Anda menulis kode dari perspektif bagaimana kode Anda akan digunakan. Dengan kata lain, saat mempraktikkan pengembangan berbasis pengujian, Anda terus-menerus menulis pengujian Anda dari perspektif pengguna. Oleh karena itu, pengembangan berbasis pengujian dapat menghasilkan API yang lebih bersih dan lebih dapat dipahami.

Akhirnya, pengembangan berbasis pengujian memaksa Anda untuk menulis pengujian unit sebagai bagian dari proses normal penulisan aplikasi. Saat tenggat waktu proyek mendekat, pengujian biasanya merupakan hal pertama yang keluar jendela. Ketika mempraktikkan pengembangan berbasis pengujian, di sisi lain, Anda lebih mungkin berbudi luhur tentang pengujian unit penulisan karena pengembangan berbasis pengujian membuat pengujian unit menjadi pusat proses membangun aplikasi.

Catatan

Untuk mempelajari lebih lanjut tentang pengembangan berbasis pengujian, saya sarankan Anda membaca buku Michael Feathers Bekerja Secara Efektif dengan Kode Warisan.

Dalam perulangan ini, kami menambahkan fitur baru ke aplikasi Contact Manager kami. Kami menambahkan dukungan untuk Grup Kontak. Anda bisa menggunakan Grup Kontak untuk menata kontak Anda ke dalam kategori seperti grup Bisnis dan Teman.

Kami akan menambahkan fungsionalitas baru ini ke aplikasi kami dengan mengikuti proses pengembangan berbasis pengujian. Kami akan menulis pengujian unit terlebih dahulu dan kami akan menulis semua kode kami terhadap tes ini.

Apa yang Akan Diuji

Seperti yang kita bahas dalam iterasi sebelumnya, Anda biasanya tidak menulis pengujian unit untuk logika akses data atau melihat logika. Anda tidak menulis pengujian unit untuk logika akses data karena mengakses database adalah operasi yang relatif lambat. Anda tidak menulis pengujian unit untuk melihat logika karena mengakses tampilan memerlukan pemutaran server web yang merupakan operasi yang relatif lambat. Anda tidak boleh menulis pengujian unit kecuali tes dapat dijalankan berulang-ulang dengan sangat cepat

Karena pengembangan berbasis pengujian didorong oleh pengujian unit, kami awalnya fokus pada penulisan pengontrol dan logika bisnis. Kami menghindari menyentuh database atau tampilan. Kami tidak akan memodifikasi database atau membuat tampilan kami sampai akhir tutorial ini. Kita mulai dengan apa yang dapat diuji.

Membuat Cerita Pengguna

Saat mempraktikkan pengembangan berbasis pengujian, Anda selalu mulai dengan menulis tes. Ini segera menimbulkan pertanyaan: Bagaimana Anda memutuskan tes apa yang akan ditulis terlebih dahulu? Untuk menjawab pertanyaan ini, Anda harus menulis sekumpulan cerita pengguna.

Cerita pengguna adalah deskripsi yang sangat singkat (biasanya satu kalimat) tentang persyaratan perangkat lunak. Ini harus berupa deskripsi non-teknis tentang persyaratan yang ditulis dari perspektif pengguna.

Berikut adalah kumpulan cerita pengguna yang menjelaskan fitur yang diperlukan oleh fungsionalitas Grup Kontak baru:

  1. Pengguna dapat melihat daftar grup kontak.
  2. Pengguna dapat membuat grup kontak baru.
  3. Pengguna dapat menghapus grup kontak yang sudah ada.
  4. Pengguna dapat memilih grup kontak saat membuat kontak baru.
  5. Pengguna dapat memilih grup kontak saat mengedit kontak yang sudah ada.
  6. Daftar grup kontak ditampilkan dalam tampilan Indeks.
  7. Saat pengguna mengklik grup kontak, daftar kontak yang cocok akan ditampilkan.

Perhatikan bahwa daftar cerita pengguna ini benar-benar dapat dimengerti oleh pelanggan. Tidak ada penyebutan detail implementasi teknis.

Saat dalam proses membangun aplikasi Anda, serangkaian cerita pengguna dapat menjadi lebih halus. Anda dapat memecah cerita pengguna menjadi beberapa cerita (persyaratan). Misalnya, Anda mungkin memutuskan bahwa membuat grup kontak baru harus melibatkan validasi. Mengirimkan grup kontak tanpa nama harus mengembalikan kesalahan validasi.

Setelah membuat daftar cerita pengguna, Anda siap untuk menulis pengujian unit pertama Anda. Kita akan mulai dengan membuat pengujian unit untuk melihat daftar grup kontak.

Mencantumkan Grup Kontak

Kisah pengguna pertama kami adalah bahwa pengguna harus dapat melihat daftar grup kontak. Kita perlu mengekspresikan cerita ini dengan tes.

Buat pengujian unit baru dengan mengklik kanan folder Pengontrol di proyek ContactManager.Tests, memilih Tambahkan, Pengujian Baru, dan pilih templat Pengujian Unit (lihat Gambar 1). Beri nama pengujian unit baru GroupControllerTest.vb dan klik tombol OK .

Menambahkan pengujian unit GroupControllerTest

Gambar 01: Menambahkan pengujian unit GroupControllerTest (Klik untuk melihat gambar ukuran penuh)

Pengujian unit pertama kami terkandung dalam Listing 1. Pengujian ini memverifikasi bahwa metode Index() pengontrol Grup mengembalikan sekumpulan Grup. Pengujian memverifikasi bahwa kumpulan Grup dikembalikan dalam data tampilan.

Daftar 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

Saat pertama kali mengetik kode di Listing 1 di Visual Studio, Anda akan mendapatkan banyak garis berlekuk merah. Kami belum membuat kelas GroupController atau Group.

Pada titik ini, kita bahkan tidak dapat membangun aplikasi kita sehingga kita tidak dapat menjalankan pengujian unit pertama kita. Itu bagus. Itu dihitung sebagai tes yang gagal. Oleh karena itu, kita sekarang memiliki izin untuk mulai menulis kode aplikasi. Kita perlu menulis kode yang cukup untuk menjalankan tes kita.

Kelas Pengontrol grup di Listing 2 berisi kode minimum yang diperlukan untuk lulus pengujian unit. Tindakan Index() mengembalikan daftar Grup yang dikodekan secara statis (kelas Grup ditentukan dalam Daftar 3).

Daftar 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

Daftar 3 - Models\Group.vb

Public Class Group

End Class

Setelah kami menambahkan kelas GroupController dan Group ke proyek kami, pengujian unit pertama kami berhasil diselesaikan (lihat Gambar 2). Kami telah melakukan pekerjaan minimum yang diperlukan untuk lulus tes. Sudah waktunya untuk merayakan.

Sukses!

Gambar 02: Sukses! (Klik untuk melihat gambar ukuran penuh)

Membuat Grup Kontak

Sekarang kita dapat melanjutkan ke cerita pengguna kedua. Kita harus dapat membuat grup kontak baru. Kita perlu mengekspresikan niat ini dengan tes.

Pengujian di Daftar 4 memverifikasi bahwa memanggil metode Create() dengan Grup baru menambahkan Grup ke daftar Grup yang dikembalikan oleh metode Index(). Dengan kata lain, jika saya membuat grup baru maka saya harus bisa mendapatkan kembali Grup baru dari daftar Grup yang dikembalikan oleh metode Index().

Daftar 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

Pengujian di Daftar 4 memanggil metode Buat() pengontrol Grup dengan Grup kontak baru. Selanjutnya, pengujian memverifikasi bahwa memanggil metode Group controller Index() mengembalikan Grup baru dalam data tampilan.

Pengontrol Grup yang dimodifikasi di Daftar 5 berisi perubahan minimum yang diperlukan untuk lulus pengujian baru.

Daftar 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

Pengontrol Grup di Daftar 5 memiliki tindakan Create() baru. Tindakan ini menambahkan Grup ke kumpulan Grup. Perhatikan bahwa tindakan Index() telah dimodifikasi untuk mengembalikan konten kumpulan Grup.

Sekali lagi, kami telah melakukan jumlah minimum pekerjaan yang diperlukan untuk lulus uji unit. Setelah kami membuat perubahan ini pada pengontrol Grup, semua pengujian unit kami lulus.

Menambahkan Validasi

Persyaratan ini tidak dinyatakan secara eksplisit dalam cerita pengguna. Namun, wajar untuk mengharuskan Grup memiliki nama. Jika tidak, mengatur kontak ke dalam Grup tidak akan sangat berguna.

Daftar 6 berisi pengujian baru yang mengekspresikan niat ini. Pengujian ini memverifikasi bahwa mencoba membuat Grup tanpa memberikan nama menghasilkan pesan kesalahan validasi dalam status model.

Daftar 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

Untuk memenuhi pengujian ini, kita perlu menambahkan properti Nama ke kelas Grup kita (lihat Daftar 7). Selain itu, kita perlu menambahkan sedikit logika validasi ke tindakan Buat() pengontrol Grup (lihat Daftar 8).

Daftar 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

Daftar 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

Perhatikan bahwa tindakan Buat() pengontrol Grup sekarang berisi validasi dan logika database. Saat ini, database yang digunakan oleh pengontrol Grup tidak lebih dari koleksi dalam memori.

Waktu untuk Refaktor

Langkah ketiga dalam Merah/Hijau/Refaktor adalah bagian Refaktor. Pada titik ini, kita perlu mundur dari kode kita dan mempertimbangkan bagaimana kita dapat merefaktor aplikasi kita untuk meningkatkan desainnya. Tahap Refaktor adalah tahap di mana kami berpikir keras tentang cara terbaik untuk menerapkan prinsip dan pola desain perangkat lunak.

Kami bebas untuk memodifikasi kode kami dengan cara apa pun yang kami pilih untuk meningkatkan desain kode. Kami memiliki jaring pengaman pengujian unit yang mencegah kami merusak fungsionalitas yang ada.

Saat ini, pengontrol Grup kami berantakan dari perspektif desain perangkat lunak yang baik. Pengontrol Grup berisi berantakan kusut validasi dan kode akses data. Untuk menghindari pelanggaran Prinsip Tanggung Jawab Tunggal, kita perlu memisahkan kekhawatiran ini ke dalam kelas yang berbeda.

Kelas pengontrol Grup kami yang direfaktor terkandung dalam Daftar 9. Pengontrol telah dimodifikasi untuk menggunakan lapisan layanan ContactManager. Ini adalah lapisan layanan yang sama dengan yang kami gunakan dengan pengontrol Kontak.

Daftar 10 berisi metode baru yang ditambahkan ke lapisan layanan ContactManager untuk mendukung validasi, daftar, dan pembuatan grup. Antarmuka IContactManagerService diperbarui untuk menyertakan metode baru.

Daftar 11 berisi kelas FakeContactManagerRepository baru yang mengimplementasikan antarmuka IContactManagerRepository. Tidak seperti kelas EntityContactManagerRepository yang juga mengimplementasikan antarmuka IContactManagerRepository, kelas FakeContactManagerRepository baru kami tidak berkomunikasi dengan database. Kelas FakeContactManagerRepository menggunakan koleksi dalam memori sebagai proksi untuk database. Kami akan menggunakan kelas ini dalam pengujian unit kami sebagai lapisan repositori palsu.

Daftar 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

Daftar 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

Daftar 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

Memodifikasi antarmuka IContactManagerRepository memerlukan penggunaan untuk mengimplementasikan metode CreateGroup() dan ListGroups() di kelas EntityContactManagerRepository. Cara termalukan dan tercepat untuk melakukan ini adalah dengan menambahkan metode ganja yang terlihat seperti ini:

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

Akhirnya, perubahan pada desain aplikasi kami mengharuskan kami untuk melakukan beberapa modifikasi pada pengujian unit kami. Kita sekarang perlu menggunakan FakeContactManagerRepository saat melakukan pengujian unit. Kelas GroupControllerTest yang diperbarui terkandung dalam Listing 12.

Daftar 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

Setelah kita membuat semua perubahan ini, sekali lagi, semua pengujian unit kita lulus. Kami telah menyelesaikan seluruh siklus Merah/Hijau/Refaktor. Kami telah menerapkan dua cerita pengguna pertama. Kami sekarang memiliki pengujian unit pendukung untuk persyaratan yang dinyatakan dalam cerita pengguna. Menerapkan sisa cerita pengguna melibatkan pengulangan siklus Merah/Hijau/Refaktor yang sama.

Memodifikasi Database kami

Sayangnya, meskipun kami telah memenuhi semua persyaratan yang dinyatakan oleh pengujian unit kami, pekerjaan kami tidak dilakukan. Kita masih perlu memodifikasi database kita.

Kita perlu membuat tabel database Grup baru. Ikuti langkah-langkah berikut:

  1. Di jendela Penjelajah Server, klik kanan folder Tabel dan pilih opsi menu Tambahkan Tabel Baru.
  2. Masukkan dua kolom yang dijelaskan di bawah ini dalam Designer Tabel.
  3. Tandai kolom Id sebagai kunci primer dan kolom Identitas.
  4. Simpan tabel baru dengan nama Grup dengan mengklik ikon floppy.

Nama Kolom Jenis Data Perbolehkan Null
Id int FALSE
Nama nvarchar(50) Salah

Selanjutnya, kita perlu menghapus semua data dari tabel Kontak (jika tidak, kita tidak akan dapat membuat hubungan antara tabel Kontak dan Grup). Ikuti langkah-langkah berikut:

  1. Klik kanan tabel Kontak dan pilih opsi menu Perlihatkan Data Tabel.
  2. Hapus semua baris.

Selanjutnya, kita perlu menentukan hubungan antara tabel database Grup dan tabel database Kontak yang sudah ada. Ikuti langkah-langkah berikut:

  1. Klik dua kali tabel Kontak di jendela Penjelajah Server untuk membuka Designer Tabel.
  2. Tambahkan kolom bilangan bulat baru ke tabel Kontak bernama GroupId.
  3. Klik tombol Hubungan untuk membuka dialog Hubungan Kunci Asing (lihat Gambar 3).
  4. Klik tombol Tambahkan.
  5. Klik tombol elipsis yang muncul di samping tombol Spesifikasi Tabel dan Kolom.
  6. Dalam dialog Tabel dan Kolom, pilih Grup sebagai tabel kunci utama dan Id sebagai kolom kunci utama. Pilih Kontak sebagai tabel kunci asing dan GroupId sebagai kolom kunci asing (lihat Gambar 4). Klik tombol Ok.
  7. Di bawah SISIPKAN dan PERBARUI Spesifikasi, pilih nilai Kaskade untuk Hapus Aturan.
  8. Klik tombol Tutup untuk menutup dialog Hubungan Kunci Asing.
  9. Klik tombol Simpan untuk menyimpan perubahan ke tabel Kontak.

Membuat hubungan tabel database

Gambar 03: Membuat hubungan tabel database (Klik untuk melihat gambar ukuran penuh)

Menentukan hubungan tabel

Gambar 04: Menentukan hubungan tabel (Klik untuk melihat gambar ukuran penuh)

Memperbarui Model Data kami

Selanjutnya, kita perlu memperbarui model data kita untuk mewakili tabel database baru. Ikuti langkah-langkah berikut:

  1. Klik dua kali file ContactManagerModel.edmx di folder Model untuk membuka Designer Entitas.
  2. Klik kanan permukaan Designer dan pilih opsi menu Perbarui Model dari Database.
  3. Di Panduan Pembaruan, pilih tabel Grup dan klik tombol Selesai (lihat Gambar 5).
  4. Klik kanan entitas Grup dan pilih opsi menu Ganti Nama. Ubah nama entitas Grup menjadi Grup (tunggal).
  5. Klik kanan properti navigasi Grup yang muncul di bagian bawah entitas Kontak. Ubah nama properti navigasi Grup menjadi Grup (tunggal).

Memperbarui model Kerangka Kerja Entitas dari database

Gambar 05: Memperbarui model Kerangka Kerja Entitas dari database (Klik untuk melihat gambar ukuran penuh)

Setelah Anda menyelesaikan langkah-langkah ini, model data Anda akan mewakili tabel Kontak dan Grup. Designer Entitas harus menampilkan kedua entitas (lihat Gambar 6).

Entitas Designer menampilkan Grup dan Kontak

Gambar 06: Entitas Designer menampilkan Grup dan Kontak(Klik untuk melihat gambar ukuran penuh)

Membuat Kelas Repositori kami

Selanjutnya, kita perlu mengimplementasikan kelas repositori kita. Selama iterasi ini, kami menambahkan beberapa metode baru ke antarmuka IContactManagerRepository sambil menulis kode untuk memenuhi pengujian unit kami. Versi akhir antarmuka IContactManagerRepository terkandung dalam Listing 14.

Daftar 14 - Model\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

Kami belum benar-benar menerapkan salah satu metode yang terkait dengan bekerja dengan grup kontak di kelas EntityContactManagerRepository kami yang sebenarnya. Saat ini, kelas EntityContactManagerRepository memiliki metode stub untuk setiap metode grup kontak yang tercantum di antarmuka IContactManagerRepository. Misalnya, metode ListGroups() saat ini terlihat seperti ini:

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

    throw New NotImplementedException()

End Function

Metode stub memungkinkan kami untuk mengkompilasi aplikasi kami dan lulus pengujian unit. Namun, sekarang saatnya untuk benar-benar menerapkan metode ini. Versi akhir kelas EntityContactManagerRepository terkandung dalam Listing 13.

Daftar 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

Membuat Tampilan

ASP.NET aplikasi MVC saat Anda menggunakan mesin tampilan ASP.NET default. Jadi, Anda tidak membuat tampilan sebagai respons terhadap pengujian unit tertentu. Namun, karena aplikasi tidak akan berguna tanpa tampilan, kami tidak dapat menyelesaikan iterasi ini tanpa membuat dan memodifikasi tampilan yang terkandung dalam aplikasi Contact Manager.

Kita perlu membuat tampilan baru berikut untuk mengelola grup kontak (lihat Gambar 7):

  • Views\Group\Index.aspx - Menampilkan daftar grup kontak
  • Views\Group\Delete.aspx - Menampilkan formulir konfirmasi untuk menghapus grup kontak

Tampilan Indeks Grup

Gambar 07: Tampilan Indeks Grup (Klik untuk melihat gambar ukuran penuh)

Kita perlu mengubah tampilan yang sudah ada berikut ini sehingga mereka menyertakan grup kontak:

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

Anda dapat melihat tampilan yang dimodifikasi dengan melihat aplikasi Visual Studio yang menyertai tutorial ini. Misalnya, Gambar 8 mengilustrasikan tampilan Indeks Kontak.

Tampilan Indeks Kontak

Gambar 08: Tampilan Indeks Kontak (Klik untuk melihat gambar ukuran penuh)

Ringkasan

Dalam perulangan ini, kami menambahkan fungsionalitas baru ke aplikasi Contact Manager kami dengan mengikuti metodologi desain aplikasi pengembangan berbasis pengujian. Kami mulai dengan membuat sekumpulan cerita pengguna. Kami membuat serangkaian pengujian unit yang sesuai dengan persyaratan yang dinyatakan oleh cerita pengguna. Akhirnya, kami menulis kode yang cukup untuk memenuhi persyaratan yang dinyatakan oleh pengujian unit.

Setelah selesai menulis kode yang cukup untuk memenuhi persyaratan yang dinyatakan oleh pengujian unit, kami memperbarui database dan tampilan kami. Kami menambahkan tabel Grup baru ke database kami dan memperbarui Model Data Kerangka Kerja Entitas kami. Kami juga membuat dan memodifikasi sekumpulan tampilan.

Dalam iterasi berikutnya -- iterasi akhir -- kami menulis ulang aplikasi kami untuk memanfaatkan Ajax. Dengan memanfaatkan Ajax, kami akan meningkatkan responsivitas dan performa aplikasi Contact Manager.