"
 
 
 
ASP.NET (snapshot 2017) Microsoft documentation and samples

Iteration #6 – Use test-driven development (VB)

by Microsoft

Download Code

In this sixth iteration, we add new functionality to our application by writing unit tests first and writing code against the unit tests. In this iteration, we add contact groups.

Building a Contact Management ASP.NET MVC Application (VB)

In this series of tutorials, we build an entire Contact Management application from start to finish. The Contact Manager application enables you to store contact information - names, phone numbers and email addresses - for a list of people.

We build the application over multiple iterations. With each iteration, we gradually improve the application. The goal of this multiple iteration approach is to enable you to understand the reason for each change.

This Iteration

In the previous iteration of the Contact Manager application, we created unit tests to provide a safety net for our code. The motivation for creating the unit tests was to make our code more resilient to change. With unit tests in place, we can happily make any change to our code and immediately know whether we have broken existing functionality.

In this iteration, we use unit tests for an entirely different purpose. In this iteration, we use unit tests as part of an application design philosophy called test-driven development. When you practice test-driven development, you write tests first and then write code against the tests.

More precisely, when practicing test-driven development, there are three steps that you complete when creating code (Red/ Green/Refactor):

  1. Write a unit test that fails (Red)
  2. Write code that passes the unit test (Green)
  3. Refactor your code (Refactor)

First, you write the unit test. The unit test should express your intention for how you expect your code to behave. When you first create the unit test, the unit test should fail. The test should fail because you have not yet written any application code that satisfies the test.

Next, you write just enough code in order for the unit test to pass. The goal is to write the code in the laziest, sloppiest and fastest possible way. You should not waste time thinking about the architecture of your application. Instead, you should focus on writing the minimal amount of code necessary to satisfy the intention expressed by the unit test.

Finally, after you have written enough code, you can step back and consider the overall architecture of your application. In this step, you rewrite (refactor) your code by taking advantage of software design patterns – such as the repository pattern – so that your code is more maintainable. You can fearlessly rewrite your code in this step because your code is covered by unit tests.

There are many benefits that result from practicing test-driven development. First, test-driven development forces you to focus on code that actually needs to be written. Because you are constantly focused on just writing enough code to pass a particular test, you are prevented from wandering into the weeds and writing massive amounts of code that you will never use.

Second, a “test first” design methodology forces you to write code from the perspective of how your code will be used. In other words, when practicing test-driven development, you are constantly writing your tests from a user perspective. Therefore, test-driven development can result in cleaner and more understandable APIs.

Finally, test-driven development forces you to write unit tests as part of the normal process of writing an application. As a project deadline approaches, testing is typically the first thing that goes out the window. When practicing test-driven development, on the other hand, you are more likely to be virtuous about writing unit tests because test-driven development makes unit tests central to the process of building an application.

[!NOTE]

To learn more about test-driven development, I recommend that you read Michael Feathers book Working Effectively with Legacy Code.

In this iteration, we add a new feature to our Contact Manager application. We add support for Contact Groups. You can use Contact Groups to organize your contacts into categories such as Business and Friend groups.

We’ll add this new functionality to our application by following a process of test-driven development. We’ll write our unit tests first and we’ll write all of our code against these tests.

What Gets Tested

As we discussed in the previous iteration, you typically do not write unit tests for data access logic or view logic. You don t write unit tests for data access logic because accessing a database is a relatively slow operation. You don t write unit tests for view logic because accessing a view requires spinning up a web server which is a relatively slow operation. You shouldn t write a unit test unless the test can be executed over and over again very fast

Because test-driven development is driven by unit tests, we focus initially on writing controller and business logic. We avoid touching the database or views. We won t modify the database or create our views until the very end of this tutorial. We start with what can be tested.

Creating User Stories

When practicing test-driven development, you always start by writing a test. This immediately raises the question: How do you decide what test to write first? To answer this question, you should write a set of user stories.

A user story is a very brief (usually one sentence) description of a software requirement. It should be a non-technical description of a requirement written from a user perspective.

Here s the set of user stories that describe the features required by the new Contact Group functionality:

  1. User can view a list of contact groups.
  2. User can create a new contact group.
  3. User can delete an existing contact group.
  4. User can select a contact group when creating a new contact.
  5. User can select a contact group when editing an existing contact.
  6. A list of contact groups is displayed in the Index view.
  7. When a user clicks a contact group, a list of matching contacts is displayed.

Notice that this list of user stories is completely understandable by a customer. There is no mention of technical implementation details.

While in the process of building your application, the set of user stories can become more refined. You might break a user story into multiple stories (requirements). For example, you might decide that creating a new contact group should involve validation. Submitting a contact group without a name should return a validation error.

After you create a list of user stories, you are ready to write your first unit test. We’ll start by creating a unit test for viewing the list of contact groups.

Listing Contact Groups

Our first user story is that a user should be able to view a list of contact groups. We need to express this story with a test.

Create a new unit test by right-clicking the Controllers folder in the ContactManager.Tests project, selecting Add, New Test, and selecting the Unit Test template (see Figure 1). Name the new unit test GroupControllerTest.vb and click the OK button.

Adding the GroupControllerTest unit test

Figure 01: Adding the GroupControllerTest unit test(Click to view full-size image)

Our first unit test is contained in Listing 1. This test verifies that the Index() method of the Group controller returns a set of Groups. The test verifies that a collection of Groups is returned in view data.

Listing 1 - Controllers.vb

[!code-vbMain]

   1:  Imports Microsoft.VisualStudio.TestTools.UnitTesting
   2:  Imports System.Web.Mvc
   3:   
   4:  <TestClass()> _
   5:  Public Class GroupControllerTest
   6:   
   7:      <TestMethod()> _
   8:      Public Sub Index()
   9:          ' Arrange
  10:          Dim controller = New GroupController()
  11:   
  12:          ' Act
  13:          Dim result = CType(controller.Index(), ViewResult)
  14:   
  15:          ' Assert
  16:          Assert.IsInstanceOfType(result.ViewData.Model, GetType(IEnumerable(Of Group)))
  17:      End Sub
  18:  End Class

When you first type the code in Listing 1 in Visual Studio, you’ll get a lot of red squiggly lines. We have not created the GroupController or Group classes.

At this point, we can t even build our application so we can t execute our first unit test. That s good. That counts as a failing test. Therefore, we now have permission to start writing application code. We need to write enough code to execute our test.

The Group controller class in Listing 2 contains the bare minimum of code required to pass the unit test. The Index() action returns a statically coded list of Groups (the Group class is defined in Listing 3).

Listing 2 - Controllers.vb

[!code-vbMain]

   1:  Public Class GroupController
   2:      Inherits System.Web.Mvc.Controller
   3:   
   4:      Function Index() As ActionResult
   5:          Dim groups = new List(Of Group)
   6:          Return View(groups)
   7:      End Function
   8:   
   9:  End Class

Listing 3 - Models.vb

[!code-vbMain]

   1:  Public Class Group
   2:   
   3:  End Class

After we add the GroupController and Group classes to our project, our first unit test completes successfully (see Figure 2). We have done the minimum work required to pass the test. It is time to celebrate.

Success!

Figure 02: Success!(Click to view full-size image)

Creating Contact Groups

Now we can move on to the second user story. We need to be able to create new contact groups. We need to express this intention with a test.

The test in Listing 4 verifies that calling the Create() method with a new Group adds the Group to the list of Groups returned by the Index() method. In other words, if I create a new group then I should be able to get the new Group back from the list of Groups returned by the Index() method.

Listing 4 - Controllers.vb

[!code-vbMain]

   1:  <TestMethod> _
   2:  Public Sub Create()
   3:      ' Arrange
   4:      Dim controller = New GroupController()
   5:   
   6:      ' Act
   7:      Dim groupToCreate = New Group()
   8:      controller.Create(groupToCreate)
   9:   
  10:      ' Assert
  11:      Dim result = CType(controller.Index(), ViewResult)
  12:      Dim groups = CType(result.ViewData.Model, IEnumerable(Of Group))
  13:      CollectionAssert.Contains(groups.ToList(), groupToCreate)
  14:  End Sub

The test in Listing 4 calls the Group controller Create() method with a new contact Group. Next, the test verifies that calling the Group controller Index() method returns the new Group in view data.

The modified Group controller in Listing 5 contains the bare minimum of changes required to pass the new test.

Listing 5 - Controllers.vb

[!code-vbMain]

   1:  Public Class GroupController
   2:  Inherits Controller
   3:   
   4:  Private _groups As IList(Of Group) = New List(Of Group)()
   5:   
   6:  Public Function Index() As ActionResult
   7:      Return View(_groups)
   8:  End Function
   9:   
  10:  Public Function Create(ByVal groupToCreate As Group) As ActionResult
  11:      _groups.Add(groupToCreate)
  12:      Return RedirectToAction("Index")
  13:   
  14:  End Function
  15:  End Class

The Group controller in Listing 5 has a new Create() action. This action adds a Group to a collection of Groups. Notice that the Index() action has been modified to return the contents of the collection of Groups.

Once again, we have performed the bare minimum amount of work required to pass the unit test. After we make these changes to the Group controller, all of our unit tests pass.

Adding Validation

This requirement was not stated explicitly in the user story. However, it is reasonable to require that a Group have a name. Otherwise, organizing contacts into Groups would not be very useful.

Listing 6 contains a new test that expresses this intention. This test verifies that attempting to create a Group without supplying a name results in a validation error message in model state.

Listing 6 - Controllers.vb

[!code-vbMain]

   1:  <TestMethod> _
   2:  Public Sub CreateRequiredName()
   3:      ' Arrange
   4:      Dim controller = New GroupController()
   5:   
   6:      ' Act
   7:      Dim groupToCreate As New Group()
   8:      groupToCreate.Name = String.Empty
   9:      Dim result = CType(controller.Create(groupToCreate), ViewResult)
  10:   
  11:      ' Assert
  12:      Dim [error] = result.ViewData.ModelState("Name").Errors(0)
  13:      Assert.AreEqual("Name is required.", [error].ErrorMessage)
  14:  End Sub

In order to satisfy this test, we need to add a Name property to our Group class (see Listing 7). Furthermore, we need to add a tiny bit of validation logic to our Group controller s Create() action (see Listing 8).

Listing 7 - Models.vb

[!code-vbMain]

   1:  Public Class Group
   2:   
   3:      Private _name As String
   4:   
   5:      Public Property Name() As String
   6:      Get
   7:          Return _name
   8:      End Get
   9:      Set(ByVal value As String)
  10:          _name = value
  11:      End Set
  12:  End Property
  13:   
  14:  End Class

Listing 8 - Controllers.vb

[!code-vbMain]

   1:  Public Function Create(ByVal groupToCreate As Group) As ActionResult
   2:      ' Validation logic
   3:      If groupToCreate.Name.Trim().Length = 0 Then
   4:      ModelState.AddModelError("Name", "Name is required.")
   5:      Return View("Create")
   6:      End If
   7:   
   8:      ' Database logic
   9:      _groups.Add(groupToCreate)
  10:      Return RedirectToAction("Index")
  11:  End Function

Notice that the Group controller Create() action now contains both validation and database logic. Currently, the database used by the Group controller consists of nothing more than an in-memory collection.

Time to Refactor

The third step in Red/Green/Refactor is the Refactor part. At this point, we need to step back from our code and consider how we can refactor our application to improve its design. The Refactor stage is the stage at which we think hard about the best way of implementing software design principles and patterns.

We are free to modify our code in any way that we choose to improve the design of the code. We have a safety net of unit tests that prevent us from breaking existing functionality.

Right now, our Group controller is a mess from the perspective of good software design. The Group controller contains a tangled mess of validation and data access code. To avoid violating the Single Responsibility Principle, we need to separate these concerns into different classes.

Our refactored Group controller class is contained in Listing 9. The controller has been modified to use the ContactManager service layer. This is the same service layer that we use with the Contact controller.

Listing 10 contains the new methods added to the ContactManager service layer to support validating, listing, and creating groups. The IContactManagerService interface was updated to include the new methods.

Listing 11 contains a new FakeContactManagerRepository class that implements the IContactManagerRepository interface. Unlike the EntityContactManagerRepository class that also implements the IContactManagerRepository interface, our new FakeContactManagerRepository class does not communicate with the database. The FakeContactManagerRepository class uses an in-memory collection as a proxy for the database. We’ll use this class in our unit tests as a fake repository layer.

Listing 9 - Controllers.vb

[!code-vbMain]

   1:  Public Class GroupController
   2:  Inherits Controller
   3:   
   4:  Private _service As IContactManagerService
   5:   
   6:  Public Sub New()
   7:      _service = New ContactManagerService(New ModelStateWrapper(Me.ModelState))
   8:   
   9:  End Sub
  10:   
  11:  Public Sub New(ByVal service As IContactManagerService)
  12:      _service = service
  13:  End Sub
  14:   
  15:  Public Function Index() As ActionResult
  16:      Return View(_service.ListGroups())
  17:  End Function
  18:   
  19:   
  20:  Public Function Create(ByVal groupToCreate As Group) As ActionResult
  21:      If _service.CreateGroup(groupToCreate) Then
  22:          Return RedirectToAction("Index")
  23:      End If
  24:      Return View("Create")
  25:  End Function
  26:   
  27:  End Class

Listing 10 - Controllers.vb

[!code-vbMain]

   1:  Public Function ValidateGroup(ByVal groupToValidate As Group) As Boolean
   2:  If groupToValidate.Name.Trim().Length = 0 Then
   3:      _validationDictionary.AddError("Name", "Name is required.")
   4:  End If
   5:  Return _validationDictionary.IsValid
   6:  End Function
   7:   
   8:  Public Function CreateGroup(ByVal groupToCreate As Group) As Boolean Implements IContactManagerService.CreateGroup
   9:      ' Validation logic
  10:      If Not ValidateGroup(groupToCreate) Then
  11:          Return False
  12:      End If
  13:   
  14:      ' Database logic
  15:      Try
  16:          _repository.CreateGroup(groupToCreate)
  17:      Catch
  18:          Return False
  19:      End Try
  20:      Return True
  21:  End Function
  22:   
  23:  Public Function ListGroups() As IEnumerable(Of Group) Implements IContactManagerService.ListGroups
  24:      Return _repository.ListGroups()
  25:  End Function

Listing 11 - Controllers.vb

[!code-vbMain]

   1:  Public Class FakeContactManagerRepository
   2:  Implements IContactManagerRepository
   3:   
   4:  Private _groups As IList(Of Group) = New List(Of Group)()
   5:   
   6:  #Region "IContactManagerRepository Members"
   7:   
   8:  ' Group methods
   9:   
  10:  Public Function CreateGroup(ByVal groupToCreate As Group) As Group Implements IContactManagerRepository.CreateGroup
  11:      _groups.Add(groupToCreate)
  12:      Return groupToCreate
  13:  End Function
  14:   
  15:  Public Function ListGroups() As IEnumerable(Of Group) Implements IContactManagerRepository.ListGroups
  16:      Return _groups
  17:  End Function
  18:   
  19:  ' Contact methods
  20:   
  21:  Public Function CreateContact(ByVal contactToCreate As Contact) As Contact Implements IContactManagerRepository.CreateContact
  22:      Throw New NotImplementedException()
  23:  End Function
  24:   
  25:  Public Sub DeleteContact(ByVal contactToDelete As Contact) Implements IContactManagerRepository.DeleteContact
  26:      Throw New NotImplementedException()
  27:  End Sub
  28:   
  29:  Public Function EditContact(ByVal contactToEdit As Contact) As Contact Implements IContactManagerRepository.EditContact
  30:      Throw New NotImplementedException()
  31:  End Function
  32:   
  33:  Public Function GetContact(ByVal id As Integer) As Contact Implements IContactManagerRepository.GetContact
  34:      Throw New NotImplementedException()
  35:  End Function
  36:   
  37:  Public Function ListContacts() As IEnumerable(Of Contact) Implements IContactManagerRepository.ListContacts
  38:      Throw New NotImplementedException()
  39:  End Function
  40:   
  41:  #End Region
  42:  End Class

Modifying the IContactManagerRepository interface requires use to implement the CreateGroup() and ListGroups() methods in the EntityContactManagerRepository class. The laziest and fastest way to do this is to add stub methods that look like this:

[!code-vbMain]

   1:  Public Function CreateGroup(groupToCreate As Group) As Group Implements IContactManagerRepository.CreateGroup
   2:   
   3:      throw New NotImplementedException()
   4:   
   5:  End Function 
   6:   
   7:  Public Function ListGroups() As IEnumerable(Of Group) Implements IContactManagerRepository.ListGroups
   8:   
   9:      throw New NotImplementedException()
  10:   
  11:  End Function

Finally, these changes to the design of our application require us to make some modifications to our unit tests. We now need to use the FakeContactManagerRepository when performing the unit tests. The updated GroupControllerTest class is contained in Listing 12.

Listing 12 - Controllers.vb

[!code-vbMain]

   1:  Imports Microsoft.VisualStudio.TestTools.UnitTesting
   2:  Imports System.Web.Mvc
   3:   
   4:  <TestClass()> _
   5:  Public Class GroupControllerTest
   6:   
   7:      Private _repository As IContactManagerRepository
   8:      Private _modelState As ModelStateDictionary
   9:      Private _service As IContactManagerService
  10:   
  11:      <TestInitialize()> _
  12:      Public Sub Initialize()
  13:          _repository = New FakeContactManagerRepository()
  14:          _modelState = New ModelStateDictionary()
  15:          _service = New ContactManagerService(New ModelStateWrapper(_modelState), _repository)
  16:      End Sub
  17:   
  18:      <TestMethod()> _
  19:      Public Sub Index()
  20:          ' Arrange
  21:          Dim controller = New GroupController(_service)
  22:   
  23:          ' Act
  24:          Dim result = CType(controller.Index(), ViewResult)
  25:   
  26:          ' Assert
  27:          Assert.IsInstanceOfType(result.ViewData.Model, GetType(IEnumerable(Of Group)))
  28:      End Sub
  29:   
  30:      <TestMethod()> _
  31:      Public Sub Create()
  32:          ' Arrange
  33:          Dim controller = New GroupController(_service)
  34:   
  35:          ' Act
  36:          Dim groupToCreate = New Group()
  37:          groupToCreate.Name = "Business"
  38:          controller.Create(groupToCreate)
  39:   
  40:          ' Assert
  41:          Dim result = CType(controller.Index(), ViewResult)
  42:          Dim groups = CType(result.ViewData.Model, IEnumerable(Of Group))
  43:          CollectionAssert.Contains(groups.ToList(), groupToCreate)
  44:      End Sub
  45:   
  46:      <TestMethod()> _
  47:      Public Sub CreateRequiredName()
  48:          ' Arrange
  49:          Dim controller = New GroupController(_service)
  50:   
  51:          ' Act
  52:          Dim groupToCreate = New Group()
  53:          groupToCreate.Name = String.Empty
  54:          Dim result = CType(controller.Create(groupToCreate), ViewResult)
  55:   
  56:          ' Assert
  57:          Dim nameError = _modelState("Name").Errors(0)
  58:          Assert.AreEqual("Name is required.", nameError.ErrorMessage)
  59:      End Sub
  60:   
  61:  End Class

After we make all of these changes, once again, all of our unit tests pass. We have completed the entire cycle of Red/Green/Refactor. We have implemented the first two user stories. We now have supporting unit tests for the requirements expressed in the user stories. Implementing the remainder of the user stories involves repeating the same cycle of Red/Green/Refactor.

Modifying our Database

Unfortunately, even though we have satisfied all of the requirements expressed by our unit tests, our work is not done. We still need to modify our database.

We need to create a new Group database table. Follow these steps:

  1. In the Server Explorer window, right-click the Tables folder and select the menu option Add New Table.
  2. Enter the two columns described below in the Table Designer.
  3. Mark the Id column as a primary key and Identity column.
  4. Save the new table with the name Groups by clicking the icon of the floppy.

Column Name Data Type Allow Nulls
Id int False
Name nvarchar(50) False

Next, we need to delete all of the data from the Contacts table (otherwise, we won t be able to create a relationship between the Contacts and Groups tables). Follow these steps:

  1. Right-click the Contacts table and select the menu option Show Table Data.
  2. Delete all of the rows.

Next, we need to define a relationship between the Groups database table and the existing Contacts database table. Follow these steps:

  1. Double-click the Contacts table in the Server Explorer window to open the Table Designer.
  2. Add a new integer column to the Contacts table named GroupId.
  3. Click the Relationship button to open the Foreign Key Relationships dialog (see Figure 3).
  4. Click the Add button.
  5. Click the ellipsis button that appears next to the Table and Columns Specification button.
  6. In the Tables and Columns dialog, select Groups as the primary key table and Id as the primary key column. Select Contacts as the foreign key table and GroupId as the foreign key column (see Figure 4). Click the OK button.
  7. Under INSERT and UPDATE Specification, select the value Cascade for Delete Rule.
  8. Click the Close button to close the Foreign Key Relationships dialog.
  9. Click the Save button to save the changes to the Contacts table.

Creating a database table relationship

Figure 03: Creating a database table relationship (Click to view full-size image)

Specifying table relationships

Figure 04: Specifying table relationships(Click to view full-size image)

Updating our Data Model

Next, we need to update our data model to represent the new database table. Follow these steps:

  1. Double-click the ContactManagerModel.edmx file in the Models folder to open the Entity Designer.
  2. Right-click the Designer surface and select the menu option Update Model from Database.
  3. In the Update Wizard, select the Groups table and click the Finish button (see Figure 5).
  4. Right-click the Groups entity and select the menu option Rename. Change the name of the Groups entity to Group (singular).
  5. Right-click the Groups navigation property that appears at the bottom of the Contact entity. Change the name of the Groups navigation property to Group (singular).

Updating an Entity Framework model from the database

Figure 05: Updating an Entity Framework model from the database(Click to view full-size image)

After you complete these steps, your data model will represent both the Contacts and Groups tables. The Entity Designer should show both entities (see Figure 6).

Entity Designer displaying Group and Contact

Figure 06: Entity Designer displaying Group and Contact(Click to view full-size image)

Creating our Repository Classes

Next, we need to implement our repository class. Over the course of this iteration, we added several new methods to the IContactManagerRepository interface while writing code to satisfy our unit tests. The final version of the IContactManagerRepository interface is contained in Listing 14.

Listing 14 - Models.vb

[!code-vbMain]

   1:  Public Interface IContactManagerRepository
   2:  ' Contact methods
   3:  Function CreateContact(ByVal groupId As Integer, ByVal contactToCreate As Contact) As Contact
   4:  Sub DeleteContact(ByVal contactToDelete As Contact)
   5:  Function EditContact(ByVal groupId As Integer, ByVal contactToEdit As Contact) As Contact
   6:  Function GetContact(ByVal id As Integer) As Contact
   7:   
   8:  ' Group methods
   9:  Function CreateGroup(ByVal groupToCreate As Group) As Group
  10:  Function ListGroups() As IEnumerable(Of Group)
  11:  Function GetGroup(ByVal groupId As Integer) As Group
  12:  Function GetFirstGroup() As Group
  13:  Sub DeleteGroup(ByVal groupToDelete As Group)
  14:   
  15:  End Interface

We haven t actually implemented any of the methods related to working with contact groups in our real EntityContactManagerRepository class. Currently, the EntityContactManagerRepository class has stub methods for each of the contact group methods listed in the IContactManagerRepository interface. For example, the ListGroups() method currently looks like this:

[!code-vbMain]

   1:  Public Function ListGroups() As IEnumerable(Of Group) Implements IContactManagerRepository.ListGroups
   2:   
   3:      throw New NotImplementedException()
   4:   
   5:  End Function

The stub methods enabled us to compile our application and pass the unit tests. However, now it is time to actually implement these methods. The final version of the EntityContactManagerRepository class is contained in Listing 13.

Listing 13 - Models.vb

[!code-vbMain]

   1:  Public Class EntityContactManagerRepository
   2:  Implements IContactManagerRepository
   3:   
   4:  Private _entities As New ContactManagerDBEntities()
   5:   
   6:  ' Contact methods
   7:   
   8:  Public Function GetContact(ByVal id As Integer) As Contact Implements IContactManagerRepository.GetContact
   9:      Return (From c In _entities.ContactSet.Include("Group") _
  10:              Where c.Id = id _
  11:              Select c).FirstOrDefault()
  12:  End Function
  13:   
  14:  Public Function CreateContact(ByVal groupId As Integer, ByVal contactToCreate As Contact) As Contact Implements IContactManagerRepository.CreateContact
  15:      ' Associate group with contact
  16:      contactToCreate.Group = GetGroup(groupId)
  17:   
  18:      ' Save new contact
  19:      _entities.AddToContactSet(contactToCreate)
  20:      _entities.SaveChanges()
  21:      Return contactToCreate
  22:  End Function
  23:   
  24:  Public Function EditContact(ByVal groupId As Integer, ByVal contactToEdit As Contact) As Contact Implements IContactManagerRepository.EditContact
  25:      ' Get original contact
  26:      Dim originalContact = GetContact(contactToEdit.Id)
  27:   
  28:      ' Update with new group
  29:      originalContact.Group = GetGroup(groupId)
  30:   
  31:      ' Save changes
  32:      _entities.ApplyPropertyChanges(originalContact.EntityKey.EntitySetName, contactToEdit)
  33:      _entities.SaveChanges()
  34:      Return contactToEdit
  35:  End Function
  36:   
  37:  Public Sub DeleteContact(ByVal contactToDelete As Contact) Implements IContactManagerRepository.DeleteContact 
  38:      Dim originalContact = GetContact(contactToDelete.Id)
  39:      _entities.DeleteObject(originalContact)
  40:      _entities.SaveChanges()
  41:  End Sub
  42:   
  43:      ' Group methods
  44:   
  45:  Public Function CreateGroup(ByVal groupToCreate As Group) As Group Implements IContactManagerRepository.CreateGroup 
  46:      _entities.AddToGroupSet(groupToCreate)
  47:      _entities.SaveChanges()
  48:      Return groupToCreate
  49:  End Function
  50:   
  51:  Public Function ListGroups() As IEnumerable(Of Group) Implements IContactManagerRepository.ListGroups
  52:      Return _entities.GroupSet.ToList()
  53:  End Function
  54:   
  55:  Public Function GetFirstGroup() As Group Implements IContactManagerRepository.GetFirstGroup
  56:      Return _entities.GroupSet.Include("Contacts").FirstOrDefault()
  57:  End Function
  58:   
  59:  Public Function GetGroup(ByVal id As Integer) As Group Implements IContactManagerRepository.GetGroup
  60:      Return (From g In _entities.GroupSet.Include("Contacts") _
  61:              Where g.Id = id _
  62:              Select g).FirstOrDefault()
  63:  End Function
  64:   
  65:  Public Sub DeleteGroup(ByVal groupToDelete As Group) Implements IContactManagerRepository.DeleteGroup
  66:      Dim originalGroup = GetGroup(groupToDelete.Id)
  67:      _entities.DeleteObject(originalGroup)
  68:      _entities.SaveChanges()
  69:  End Sub
  70:   
  71:  End Class

Creating the Views

ASP.NET MVC application when you use the default ASP.NET view engine. So, you don t create views in response to a particular unit test. However, because an application would be useless without views, we can t complete this iteration without creating and modifying the views contained in the Contact Manager application.

We need to create the following new views for managing contact groups (see Figure 7):

The Group Index view

Figure 07: The Group Index view(Click to view full-size image)

We need to modify the following existing views so that they include contact groups:

You can see the modified views by looking at the Visual Studio application that accompanies this tutorial. For example, Figure 8 illustrates the Contact Index view.

The Contact Index view

Figure 08: The Contact Index view(Click to view full-size image)

Summary

In this iteration, we added new functionality to our Contact Manager application by following a test-driven development application design methodology. We started by creating a set of user stories. We created a set of unit tests that corresponds to the requirements expressed by the user stories. Finally, we wrote just enough code to satisfy the requirements expressed by the unit tests.

After we finished writing enough code to satisfy the requirements expressed by the unit tests, we updated our database and views. We added a new Groups table to our database and updated our Entity Framework Data Model. We also created and modified a set of views.

In the next iteration – the final iteration – we rewrite our application to take advantage of Ajax. By taking advantage of Ajax, we’ll improve the responsiveness and performance of the Contact Manager application.

Previous Next





Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19>  <20>  <21>  <22>  <23
Link to this page: //www.vb-net.com/AspNet-DocAndSamples-2017/aspnet/mvc/overview/older-versions-1/contact-manager/iteration-6-use-test-driven-development-vb.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <CHAT ME>  <ABOUT ME>  < THANKS ME>