Upload
others
View
3
Download
0
Embed Size (px)
Citation preview
Visual Studio 2010 Testing Labs Page 1 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Visual Studio 2010 Testing By Benjamin Day
Visual Studio 2010 Testing Labs Page 2 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Contents Lab 01: A Deck of Cards using Test-Driven Development ......................................................................... 4
Card Class: Write Unit Test for the Constructor ....................................................................................... 8
Card Class: Compile the Code ................................................................................................................... 8
Card Class: Write Enough Code to Make It Compile ................................................................................. 9
Card Class: Run the Test .......................................................................................................................... 11
Card Class: Write Enough Code to Make the Test Pass .......................................................................... 13
Deck Class: Write the Tests ..................................................................................................................... 14
Deck Class: Enough Code to Compile ...................................................................................................... 16
Deck Class: Run the Tests ........................................................................................................................ 17
Deck Class: Implement Enough Code to Make the Tests Pass................................................................ 19
Lab 02: Unit Test the Adapter Pattern..................................................................................................... 24
Overview of the Solution ........................................................................................................................ 24
Person to DataRow: Write the Tests ....................................................................................................... 27
Person to DataRow: Enough Code to Compile and Run the Test ........................................................... 28
Person to DataRow: Enough Code to Pass the Test ................................................................................ 29
DataRow to Person: Write the Tests ....................................................................................................... 31
DataRow to Person: Enough Code to Compile and Run the Test ........................................................... 32
DataRow to Person: Enough Code to Pass the Test ................................................................................ 33
Lab 03: Unit Test the Repository Pattern ................................................................................................ 34
Prerequisite ............................................................................................................................................. 34
Overview of the Solution ........................................................................................................................ 34
Requirements for the Repository ........................................................................................................... 37
Create the Database ............................................................................................................................... 37
Brainstorming the Tests .......................................................................................................................... 39
Save: Write the Test ................................................................................................................................ 40
Save: Write Enough Code to Compile ..................................................................................................... 42
Save: Write Enough Code to Make the Tests Pass.................................................................................. 44
Get By Id: Write & Run the Test .............................................................................................................. 47
Get By Id: Enough Code to Make the Test Pass ...................................................................................... 48
Get List: Write the Test and Run It .......................................................................................................... 50
Visual Studio 2010 Testing Labs Page 3 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Modify Person: Write & Run the Test ..................................................................................................... 51
Lab 04: Test Logic in Isolation Using Mock Objects ................................................................................. 54
Introduction ............................................................................................................................................ 54
Lab Overview .......................................................................................................................................... 54
Brainstorming the Tests .......................................................................................................................... 55
Create a Mock ICustomerRepository ...................................................................................................... 55
Create Suggested Account Code: Write the Test and Implementation .................................................. 57
Create Account Code: Write the Test ..................................................................................................... 59
Create Account Code: Write Enough Code to Compile .......................................................................... 60
Create Account Code: Write Enough to Make the Test Pass .................................................................. 62
Retry Logic: Write the Test ..................................................................................................................... 63
Retry Logic: Implement the Changes to MockCustomerRepository ....................................................... 65
Retry Logic: Write Enough to Make the Test Pass .................................................................................. 67
Multiple Retry Logic: Write the Test ....................................................................................................... 69
Multiple Retry Logic: Write Enough to Make the Test Pass ................................................................... 72
Lab 05: Unit Test a User Interface Using the Model-View-Presenter Pattern ........................................ 74
Prerequisite ............................................................................................................................................. 74
Introduction ............................................................................................................................................ 74
Requirements .......................................................................................................................................... 75
The Unit Tests & Code Structure ............................................................................................................ 76
Write the Tests ........................................................................................................................................ 78
Implement the CustomerSearchPresenter Code .................................................................................... 81
Run the Tests .......................................................................................................................................... 83
Implement the User Interface ................................................................................................................ 83
Run the Web Application ........................................................................................................................ 86
Visual Studio 2010 Testing Labs Page 4 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Lab 01: A Deck of Cards using Test-Driven Development
This lab will walk you through the process of creating a class library representing a deck of playing cards
using “test first”, test-driven development.
Remember the process for doing “test first” described in William Wake’s “Extreme Programming
Explored” (Addison-Wesley):
1. Write the test code 2. Compile the test code Fails because there’s no implementation 3. Implement just enough to compile 4. Run the test Fails 5. Implement enough code to make the test pass 6. Run the test Pass 7. Refactor for clarity and to eliminate duplication 8. Repeat
There is an implied “step 0” which is to understand the requirements and decide what the basic tests
should be. The goal is to avoid the “just write code” mindset and always have a plan for what you need
to do, how you’re going to do it, and how to know when you’re done.
What are some of the rules, attributes, and requirements for a deck of cards? A deck of playing cards
has 4 “suits” – Clubs, Diamonds, Hearts, and Spades – and 13 possible “rank” values – Ace, 2, 3, 4, 5, 6,
7, 8, 9, 10, Jack, Queen, King. A valid deck is made up of 52 cards and each card is unique in the deck.
Thinking about those requirements, it sounds like we have two classes: Card and Deck. Deck is a
collection of Card objects. The Card object has two properties: Suit and Rank. Suit and Rank both have
well defined values – those will probably end being enums. We’re going to need to know how many
cards are in a deck. We’ll also need to know if one card is equal to another.
Visual Studio 2010 Testing Labs Page 5 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Now that we have a good understanding of the requirements, we’ll start by creating a unit test in our
project.
- Open DeckOfCards.sln in Visual Studio 2010
- Right-click the DeckOfCards.UnitTests project, expand the Add menu, and choose Unit Test…
Visual Studio 2010 Testing Labs Page 6 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
You should now see the Add New Test dialog. This dialog lets you choose the type of test that you want
to create. In this case, you’ll be creating a Basic Unit Test.
- Choose Basic Unit Test
- In the Test Name box, Enter CardFixture
- Click the OK button
Visual Studio 2010 Testing Labs Page 7 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
You should now see new CardFixture.cs file with a method inside named, TestMethod1. This is the
basic template that Visual Studio creates for you but we won’t be using the TestMethod1 method so you
can delete it.
- Delete the TestMethod1() method including its [TestMethod] attribute.
Visual Studio 2010 Testing Labs Page 8 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Card Class: Write Unit Test for the Constructor
Since we’ll be creating Card objects with specific Suit and Rank values, we need to write a test that
verifies that the new objects have been initialized properly.
[TestMethod]
public void CardConstructorInitializesSuitAndRankFields()
{
var card = new Card(Suit.Diamond, Rank.Two);
Assert.AreEqual<Suit>(Suit.Diamond,
card.Suit,
"Wrong suit.");
Assert.AreEqual<Rank>(Rank.Two,
card.Rank,
"Wrong rank.");
}
- Add a method to the CardFixture.cs file with the code shown above
Card Class: Compile the Code If you’re following the Test-Driven Development process, the next step is to attempt to compile this
code.
- From the Visual Studio main menu, choose Build and then Build DeckOfCards.UnitTests
It probably isn’t a surprise that the code doesn’t compile and that there are lots of errors because there
isn’t any implementation yet.
Visual Studio 2010 Testing Labs Page 9 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Card Class: Write Enough Code to Make It Compile
The next step is to create enough implementation so that the code will compile.
First, create an enumeration for Rank.
- Create a Rank enumeration in the DeckOfCards.UnitTests namespace as shown above
Next, create an enumeration for Suit.
- Create a Suit enumeration in the DeckOfCards.UnitTests namespace as shown above
Visual Studio 2010 Testing Labs Page 10 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The last thing we need in order to make the code compile is to create a Card class.
- Create a Card class in the DeckOfCards.UnitTests namespace as shown above
Now when you recompile the code, the build should succeed.
- Compile the code and verify that there are no errors
Visual Studio 2010 Testing Labs Page 11 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Card Class: Run the Test
Now that the code compiles, you’re ready to run the unit test. You can do this from the Test View
window.
- Go to the Test menu, go to Windows, and choose Test View
Visual Studio 2010 Testing Labs Page 12 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
You should now see the Test View dialog. This dialog allows you to run your unit tests.
- Verify that the CardConstructorInitializesSuitAndRankFields test is selected
- Click the Run Selection button as shown in the screenshot above
After the test run completes, you should see that the test has failed.
Visual Studio 2010 Testing Labs Page 13 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
To see what failed, you can either look at the Error Message column on the Test Results window or
double-click on the failed test to bring up the execution details viewer. The current error should be
something like “Assert.AreEqual failed. Expected:<Diamond>. Actual:<Club>. Wrong suit.”
This is telling us that, although the code compiles, we still need to write more code to make the test
pass.
Card Class: Write Enough Code to Make the Test Pass
Pretty much all that is missing is a few lines of code in the constructor on the Card class.
- Go to the Card class and add the two missing lines as shown above
Now re-run the code and verify that the test passes.
- Go to the Test View window and re-run the test
Visual Studio 2010 Testing Labs Page 14 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The test should now pass.
Deck Class: Write the Tests
Now that we have the Card class basically written, we’ll start working on the Deck class.
Remember the rules:
1. The deck cannot have more than 52 cards
2. All of the cards must be unique
3. A complete deck will have 52 cards
So we’ll need to Add cards to a Deck class and then let’s also say that the Deck has an option to
completely initialize itself and create a complete deck of cards.
This implies a handful of new unit tests for the Deck class:
1. Test to make sure that the Deck is empty
2. We’ll need a way to add cards to the deck, so there will be a unit test to Add cards
3. A unit test to ensure that the Deck rejects duplicate cards
4. A test to verify that the Deck has initialized itself
Visual Studio 2010 Testing Labs Page 15 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
[TestMethod]
public void AnEmptyDeckHasZeroCards()
{
var deck = new Deck();
Assert.AreEqual<int>(0,
deck.CardCount,
"Card count should be 0.");
}
[TestMethod]
public void AddingUniqueCardIncrementsTheCardCount()
{
var deck = new Deck();
deck.Add(new Card(Suit.Diamond, Rank.Two));
Assert.AreEqual<int>(1,
deck.CardCount,
"Card count was wrong.");
}
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void AddingDuplicateCardThrowsException()
{
var deck = new Deck();
deck.Add(new Card(Suit.Diamond, Rank.Two));
// add a duplicate card
deck.Add(new Card(Suit.Diamond, Rank.Two));
}
[TestMethod]
public void InitializeCreates52Cards()
{
var deck = new Deck();
deck.Initialize();
Assert.AreEqual<int>(52,
deck.CardCount,
"Card count was wrong.");
}
- Create the new tests in DeckFixture.cs as shown above
Visual Studio 2010 Testing Labs Page 16 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Deck Class: Enough Code to Compile
For the next step you should try to compile the code. Once again, it won’t compile because you don’t
have any implementation for Deck but once you’ve verified that it doesn’t compile, then you should
write enough code to make it compile.
public class Deck
{
public void Add(Card card)
{
}
public int CardCount { get; set; }
public void Initialize()
{
}
}
- Create a Deck class as shown above
- Compile the code
Visual Studio 2010 Testing Labs Page 17 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Deck Class: Run the Tests
At this point the code should compile and it’s time to run the tests.
- Go to the Test View window
- Select all the tests
- Click Run Selection
Visual Studio 2010 Testing Labs Page 18 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
When the test completes, you should have a number of failing tests.
Visual Studio 2010 Testing Labs Page 19 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Deck Class: Implement Enough Code to Make the Tests Pass
Let’s work on these tests one at a time starting with the AddingUniqueCardToIncrementsTheCardCount
test.
public class Deck
{
public void Add(Card card)
{
Cards.Add(card);
}
public int CardCount
{
get
{
return Cards.Count;
}
}
private List<Card> m_Cards;
public List<Card> Cards
{
get
{
if (m_Cards == null)
{
m_Cards = new List<Card>();
}
return m_Cards;
}
}
public void Initialize()
{
}
}
- Add the highlighted to the Deck class as shown above
- Re-run all the unit tests
Since we’ve implemented the Add() method, the AddingUniqueCardToIncrementsTheCardCount unit
test should be passing.
Visual Studio 2010 Testing Labs Page 20 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Next let’s work on the AddingDuplicateCardThrowsException unit test. This test verifies that we can’t
add duplicate cards to the Deck by attempting to add a duplicate card and expecting an
InvalidOperationException. If the test completes without getting that InvalidOperationException, the
test should fail.
public void Add(Card card)
{
if (IsUnique(card) == false)
{
throw new InvalidOperationException(
"Duplicate card.");
}
else
{
Cards.Add(card);
}
}
private bool IsUnique(Card card)
{
var result = (from item in Cards
where
item.Rank == card.Rank &&
item.Suit == card.Suit
select item).FirstOrDefault();
if (result == null)
{
return true;
}
else
{
return false;
}
}
- Add the highlighted code to the Deck class
- Re-run all the tests
Visual Studio 2010 Testing Labs Page 21 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The AddingDuplicateCardThrowsException test should now be passing.
Visual Studio 2010 Testing Labs Page 22 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Now let’s fix the last test – InitializeCreates52Cards.
public void Initialize()
{
Initialize(Suit.Club);
Initialize(Suit.Diamond);
Initialize(Suit.Heart);
Initialize(Suit.Spade);
}
private void Add(Suit suit, Rank rank)
{
Add(new Card(suit, rank));
}
private void Initialize(Suit suit)
{
Add(suit, Rank.Ace);
Add(suit, Rank.Two);
Add(suit, Rank.Three);
Add(suit, Rank.Four);
Add(suit, Rank.Five);
Add(suit, Rank.Six);
Add(suit, Rank.Seven);
Add(suit, Rank.Eight);
Add(suit, Rank.Nine);
Add(suit, Rank.Ten);
Add(suit, Rank.Jack);
Add(suit, Rank.Queen);
Add(suit, Rank.King);
}
- Add the highlighted code as shown above
- Re-run all the tests
Visual Studio 2010 Testing Labs Page 23 of 88
Copyright © 2013 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Once the test run has completed, you should see that all 5 unit tests are passing.
You’ve just created a Card and Deck class using Test-Driven Development.
Visual Studio 2010 Testing Labs Page 24 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Lab 02: Unit Test the Adapter Pattern
The Adapter Pattern is a design pattern that is helpful for converting one type of object to another. It’s
especially useful when used as part of data access logic. If you want to organize your data access logic
for unit testability, you’ll probably use the Repository Pattern to encapsulate the logic for calling your
database or web service and you’ll use the Adapter Pattern to encapsulate the logic for taking your data
access results and converting them to your Domain Model object (aka. Business objects).
This design not only helps testability but also ensures that your architecture follows the Single
Responsibility Principle (SRP).
Overview of the Solution
Begin by opening the solution.
- Open Visual Studio 2010
- Load DataAccessTesting_AdapterPattern.sln
Visual Studio 2010 Testing Labs Page 25 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
When the solution is loaded, you’ll see that there are 4 projects in the solution.
The Business project contains our Person Domain Model object. The DataAccess project contains the
beginnings of our data access logic. The Interfaces project contains an interface representation of our
Person object. And finally, the Tests project will hold our unit tests.
Visual Studio 2010 Testing Labs Page 26 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
In the DataAccess project, there is a PersonDataSet.xsd typed dataset that has a single DataTable in it
for Person. This is the ADO.NET class that we’ll (eventually) use to talk to the database for saves and
retrieves.
In the Interfaces project, there is an interface called IPerson that is the abstraction of our Person
business object.
The coding and testing goal of this lab is to implement and test the logic that will go into the
PersonToDataSetAdapter class in the DataAccess project that will adapt IPerson to Person DataTable
and vice versa.
Visual Studio 2010 Testing Labs Page 27 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Person to DataRow: Write the Tests
For the first tests, let’s focus on the logic to turn an IPerson instance’s data into a PersonDataRow.
[TestMethod]
public void AdaptFromPersonToDataRow()
{
IPerson fromValue = new Person();
fromValue.Id = 1234;
fromValue.FirstName = "Skip";
fromValue.LastName = "Rosenwinkle";
fromValue.EmailAddress = "[email protected]";
var toValue = new PersonDataSet().Person.NewPersonRow();
var adapter = new PersonToDataSetAdapter();
adapter.Adapt(fromValue, toValue);
AssertAreEqual(fromValue, toValue);
}
private void AssertAreEqual(
IPerson expected, PersonDataSet.PersonRow actual)
{
if (expected == null)
throw new ArgumentNullException("expected", "expected is null.");
if (actual == null)
throw new ArgumentNullException("actual", "actual is null.");
Assert.AreEqual<int>(expected.Id, actual.Id, "Id");
Assert.AreEqual<string>(expected.FirstName,
actual.FirstName, "FirstName");
Assert.AreEqual<string>(expected.LastName,
actual.LastName, "LastName");
Assert.AreEqual<string>(expected.EmailAddress,
actual.EmailAddress, "EmailAddress");
}
- Go to the DataAccessTesting.Tests project
- Open the PersonToDataSetAdapterFixture class
- Add the highlighted code as shown above
So, looking at this code, it’s not trying to do anything that’s all that fancy or exotic. It’s pretty boring
logic, actually. This is important though because it’s usually the boring and tedious code where the
errors live. If you write good unit tests for the code, you practically never have to worry about it again.
Visual Studio 2010 Testing Labs Page 28 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Person to DataRow: Enough Code to Compile and Run the Test
If you try to compile this code, it’ll fail because there’s no implementation for the Adapt() method on
PersonToDataSetAdapter.
namespace DataAccessTesting.DataAccess
{
public class PersonToDataSetAdapter
{
public void Adapt(IPerson fromValue, PersonDataSet.PersonRow toValue)
{
}
}
}
- Open PersonToDataSetAdapter.cs
- Add the highlighted code as shown above
- Compile the Solution
- Run the unit tests
The AdaptFromPersonToDataRow unit test will execute but fail.
Visual Studio 2010 Testing Labs Page 29 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The error message will be complaining that the Id property on the DataRow does not match the
expected value.
Person to DataRow: Enough Code to Pass the Test
Next, you’ll implement the adapter code to make this test actually pass.
namespace DataAccessTesting.DataAccess
{
public class PersonToDataSetAdapter
{
public void Adapt(IPerson fromValue, PersonDataSet.PersonRow toValue)
{
if (fromValue == null)
throw new ArgumentNullException("fromValue",
"fromValue is null.");
if (toValue == null)
throw new ArgumentNullException("toValue", "toValue is null.");
toValue.Id = fromValue.Id;
toValue.FirstName = fromValue.FirstName;
toValue.LastName = fromValue.LastName;
toValue.EmailAddress = fromValue.EmailAddress;
}
}
}
- Open PersonToDataSetAdapter.cs
- Add the highlighted code as shown above
- Compile the code
- Run the unit tests
Visual Studio 2010 Testing Labs Page 30 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
This time the unit test for AdaptFromPersonToDataRow should pass.
You now have working logic to take a Person object and turn it into a DataRow so that you can write it
into a database.
Visual Studio 2010 Testing Labs Page 31 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
DataRow to Person: Write the Tests
Next you’ll start working on the implementation to go in the other direction – logic to take a DataRow
and turn it into a Person. This path through the application supports the retrieval of data from a
database so that it can be consumed by the application code.
You’ll find that this next block of code is almost exactly the same as the other block of code. Boring,
isn’t it? Makes you want to fall asleep and stop paying attention, huh? Well, that’s why you need to
test this code. You’re going to make mistakes and considering how much other code is going to be built
on top of this logic, it needs to work perfectly. The unit tests will give you a high degree of confidence
that the code is working properly.
[TestMethod]
public void AdaptFromDataRowToPerson()
{
var fromValue = new PersonDataSet().Person.NewPersonRow();
fromValue.Id = 1234;
fromValue.FirstName = "Skip";
fromValue.LastName = "Rosenwinkle";
fromValue.EmailAddress = "[email protected]";
IPerson toValue = new Person();
var adapter = new PersonToDataSetAdapter();
adapter.Adapt(fromValue, toValue);
AssertAreEqual(fromValue, toValue);
}
private void AssertAreEqual(
PersonDataSet.PersonRow expected, IPerson actual)
{
if (expected == null)
throw new ArgumentNullException("expected", "expected is null.");
if (actual == null)
throw new ArgumentNullException("actual", "actual is null.");
Assert.AreEqual<int>(expected.Id, actual.Id, "Id");
Assert.AreEqual<string>(expected.FirstName,
actual.FirstName, "FirstName");
Assert.AreEqual<string>(expected.LastName,
actual.LastName, "LastName");
Assert.AreEqual<string>(expected.EmailAddress,
actual.EmailAddress, "EmailAddress");
}
- Go to PersonToDataSetAdapterFixture.cs
- Add the highlighted code as shown above
- Try to compile the code
Visual Studio 2010 Testing Labs Page 32 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
DataRow to Person: Enough Code to Compile and Run the Test
public class PersonToDataSetAdapter
{
public void Adapt(IPerson fromValue, PersonDataSet.PersonRow toValue)
{
if (fromValue == null)
throw new ArgumentNullException("fromValue", "fromValue is null.");
if (toValue == null)
throw new ArgumentNullException("toValue", "toValue is null.");
toValue.Id = fromValue.Id;
toValue.FirstName = fromValue.FirstName;
toValue.LastName = fromValue.LastName;
toValue.EmailAddress = fromValue.EmailAddress;
}
public void Adapt(PersonDataSet.PersonRow fromValue, IPerson toValue)
{
}
}
- Go to PersonToDataSetAdapter
- Add the highlighted code as shown above
- Compile the code
- Run the unit tests
The unit tests are failing because there’s no implementation.
Visual Studio 2010 Testing Labs Page 33 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
DataRow to Person: Enough Code to Pass the Test
You’re almost done. All you need is a little bit of implementation.
public void Adapt(PersonDataSet.PersonRow fromValue, IPerson toValue)
{
if (fromValue == null)
throw new ArgumentNullException("fromValue", "fromValue is null.");
if (toValue == null)
throw new ArgumentNullException("toValue", "toValue is null.");
toValue.Id = fromValue.Id;
toValue.FirstName = fromValue.FirstName;
toValue.LastName = fromValue.LastName;
toValue.EmailAddress = fromValue.EmailAddress;
}
- Go to PersonToDataSetAdapter.cs
- Add the highlighted code as shown above
- Run the unit tests
This time the unit tests pass.
You’ve implemented the basic structure of the Adapter pattern for use in your data access logic.
Visual Studio 2010 Testing Labs Page 34 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Lab 03: Unit Test the Repository Pattern
The Repository Pattern helps you to encapsulate data access logic for accessing persistent storage such
as a database or accessing a web service. In an application that uses the Domain Model Pattern (aka.
“business” objects), the Repository is responsible for 1) saving Domain Model objects to persistent
storage and 2) returning populated instances of Domain Model objects based on data retrieved from
persistent storage.
If you want to organize your data access logic for unit testability, you’ll frequently use Adapter Pattern
objects to handle the conversion of data access results into Domain Model objects and vice versa. In the
case of a database application, the Repository classes know how to generate and run SQL queries
(INSERTs, UPDATEs, DELETEs) and the Adapter pattern knows how to take ADO.NET results and convert
them to/from Domain Model classes.
This design not only helps testability but also ensures that your architecture follows the Single
Responsibility Principle (SRP).
Prerequisite
This lab assumes that you have installed SQL Server Express on your local machine and that it is available
as “(local)\SQLEXPRESS” using your current Windows login credentials.
Overview of the Solution
Begin by opening the solution.
- Open Visual Studio 2010
- Load DataAccessTesting_RepositoryPattern.sln
Visual Studio 2010 Testing Labs Page 35 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
When the solution is loaded, you’ll see that there are 4 projects in the solution.
The Business project contains our Person Domain Model object. The DataAccess project contains the
beginnings of our data access logic. The Interfaces project contains an interface representation of our
Person object. And finally, the Tests project will hold our unit tests.
Visual Studio 2010 Testing Labs Page 36 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
In the DataAccess project, there is a PersonDataSet.xsd typed dataset that has a single DataTable in it
for Person as well as a PersonTableAdapter that we’ll use for talking to the database.
In the Interfaces project, there is an interface called IPerson that is the abstraction of our Person
business object.
The coding and testing goal of this lab is to implement and test the logic that will go into the
SqlPersonRepository class in the DataAccess project. (For information about how to implement the
Adapter logic, see the “Unit Test the Adapter Pattern” lab.)
Visual Studio 2010 Testing Labs Page 37 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Requirements for the Repository
The Repository that we’re going to write and test is for loading and saving objects that implement
IPerson. The Person repository is going to need to:
1) Retrieve all person records as a list of IPerson objects
2) Get a single person record by id and return an IPerson object
3) Save an IPerson object to the database
Create the Database
This lab will be writing to a database so first you’ll need to run the initialization script.
- Under Solution Items, double-click VS2010TestinDatabaseCreate.sql to open the database
initialization script
Visual Studio 2010 Testing Labs Page 38 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The initialization script should be visible in a Visual Studio editor window similar to the one below.
- Click the Execute SQL button in the toolbar
You should now see a Connect to Database Engine dialog that will let you choose which database to run
the script against.
- In the Server name textbox, type (local)\SQLEXPRESS
- Click the Connect button
Visual Studio 2010 Testing Labs Page 39 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
If the query execution was successful, you should see a message saying that the Command(s) completed
successfully.
Brainstorming the Tests
In order to implement the requirements, we’re going to need to test at least 4 different things.
1) When we save an IPerson to the database, does the Id property on IPerson get updated with the
new @@IDENTITY value from the database
2) Can we load an IPerson by Id
3) Can we load a list of all IPerson objects
4) Making modifications to an instance of IPerson and then updating an existing record in the
database
4 unit tests:
1) SavedPersonDoesNotHaveZeroAsAnIdValue() – write a record to the database and make sure
that the Id property is updated.
2) GetPersonById() – retrieve a single IPerson by id value. This test is also essential for being able
to verify that our Save logic works because we’ll use this logic to reload saved instances from the
database and then check against what we saved.
3) GetAllPersons() – retrieve a list of all person records in the database as IPerson.
4) ModifyPerson() – write a record to the database, modify it, and then verify that the
modifications went in as a database UPDATE rather than a database INSERT.
Visual Studio 2010 Testing Labs Page 40 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Save: Write the Test
Let’s start with the SavedPersonDoesNotHaveZeroAsAnIdValue() test. This test will create an instance of
IPerson, write it to the database using the SqlPersonRepository class, and then verify that the Id value is
updated on the instance of IPerson.
- Open SqlPersonRepositoryFixture.cs
Visual Studio 2010 Testing Labs Page 41 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
[TestClass]
public class SqlPersonRepositoryFixture
{
[TestMethod]
public void SavedPersonDoesNotHaveZeroAsAnIdValue()
{
var instance = CreateAndSavePerson();
Assert.AreNotEqual<int>(0, instance.Id,
"Person Id value should not be zero.");
}
[TestInitialize]
public void Initialize()
{
m_RepositoryInstance = null;
}
private IPersonRepository m_RepositoryInstance;
public IPersonRepository RepositoryInstance
{
get
{
if (m_RepositoryInstance == null)
{
m_RepositoryInstance = new SqlPersonRepository<Person>();
}
return m_RepositoryInstance;
}
}
public IPerson CreateAndSavePerson()
{
IPerson instance = new Person();
instance.FirstName = UnitTestUtility.GetUniqueString();
instance.LastName = UnitTestUtility.GetUniqueString();
instance.EmailAddress = UnitTestUtility.GetUniqueString();
RepositoryInstance.Save(instance);
return instance;
}
[TestMethod]
public void GetPersonById()
{
Assert.Inconclusive();
}
- Add the highlighted code to SqlPersonRepositoryFixture.cs as shown above
- Compile the Solution
Visual Studio 2010 Testing Labs Page 42 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Save: Write Enough Code to Compile
At the moment, the compilation should fail because there isn’t a class named
SqlPersonRepositoryFixture.
- Add a class named SqlPersonRepository to the DataAccessTesting.DataAccess project
You should now have a SqlPersonRepository.cs class in your project as shown below.
Visual Studio 2010 Testing Labs Page 43 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Now you’ll need to create enough implementation code to make it 1) implement the IPersonRepository
interface and 2) compile.
using System;
using System.Collections.Generic;
using System.Linq;
using DataAccessTesting.Interfaces;
namespace DataAccessTesting.DataAccess
{
public class SqlPersonRepository<T> : IPersonRepository
where T : IPerson, new()
{
public IList<IPerson> GetAll()
{
throw new NotImplementedException();
}
public IPerson GetById(int id)
{
throw new NotImplementedException();
}
public void Save(IPerson saveThis)
{
}
}
}
- Add the highlighted code to SqlPersonRepository as shown above
- Compile the solution
Visual Studio 2010 Testing Labs Page 44 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Save: Write Enough Code to Make the Tests Pass
The code should compile successfully now so it’s time to run the tests.
- Run all the tests from the Test View window
The SavedPersonDoesNotHaveZeroAsAnIdValue test should be failing.
The error message from the test should be complain that “Person Id value should not be zero.”
Visual Studio 2010 Testing Labs Page 45 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
To fix this, you’ll add an implementation for the Save() method.
public void Save(IPerson saveThis)
{
if (saveThis == null)
throw new ArgumentNullException("saveThis", "saveThis is null.");
Insert(saveThis);
}
private void Insert(IPerson saveThis)
{
var dataset = new PersonDataSet();
var toValue = dataset.Person.NewPersonRow();
var adapter = new PersonToDataSetAdapter();
adapter.Adapt(saveThis, toValue);
dataset.Person.AddPersonRow(toValue);
Save(dataset);
adapter.Adapt(toValue, saveThis);
}
private void Save(PersonDataSet dataset)
{
var tableAdapter =
new PersonDataSetTableAdapters.PersonTableAdapter();
tableAdapter.Update(dataset.Person);
}
- Add the highlighted code shown above to SqlPersonRepository
- Compile the Solution
- Run all the tests
Visual Studio 2010 Testing Labs Page 46 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The test for SavedPersonDoesNotHaveZeroAsAnIdValue is now passing but the rest of the repository
tests are failing with Inconclusive.
Visual Studio 2010 Testing Labs Page 47 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Get By Id: Write & Run the Test
Next up, let’s work on the GetById() logic.
[TestMethod]
public void GetPersonById()
{
var instance = CreateAndSavePerson();
// reload the person
IPerson reloadedPerson =
RepositoryInstance.GetById(instance.Id);
AssertAreEqual(instance, reloadedPerson);
}
private void AssertAreEqual(IPerson expected, IPerson actual)
{
Assert.AreEqual<int>(expected.Id,
actual.Id,
"Id");
Assert.AreEqual<string>(expected.FirstName,
actual.FirstName,
"FirstName");
Assert.AreEqual<string>(expected.LastName,
actual.LastName,
"LastName");
Assert.AreEqual<string>(expected.EmailAddress,
actual.EmailAddress,
"EmailAddress");
}
- Go to SqlPersonRepositoryFixture.cs
- Add the highlighted test code as shown above
- Run the tests
The unit test fails because there’s no implementation.
Visual Studio 2010 Testing Labs Page 48 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Get By Id: Enough Code to Make the Test Pass
Next step, provide the implementation for GetById() in the SqlPersonRepository class.
public IPerson GetById(int id)
{
var tableAdapter =
new PersonDataSetTableAdapters.PersonTableAdapter();
var results = tableAdapter.GetDataById(id);
if (results == null || results.Count == 0)
{
return null;
}
else
{
var adapter = new PersonToDataSetAdapter();
IPerson toValue = new T();
adapter.Adapt(results[0], toValue);
return toValue;
}
}
- Go to SqlPersonRepository.cs
- Add the highlighted code as shown above
- Compile the solution
- Run the tests
Visual Studio 2010 Testing Labs Page 49 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The GetPersonById() test method is now passing.
Visual Studio 2010 Testing Labs Page 50 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Get List: Write the Test and Run It
Next up, implement the GetAllPerson() test.
[TestMethod]
public void GetAllPersons()
{
// make sure there are at least 2 person records in the database
CreateAndSavePerson();
CreateAndSavePerson();
var allPersons = RepositoryInstance.GetAll();
Assert.IsNotNull(allPersons, "Got back a null result.");
Assert.IsTrue(allPersons.Count >= 2,
"Expected at least 2 person records but there were only '{0}'.",
allPersons.Count);
}
- Go to SqlPersonRepositoryFixture.cs
- Add the highlighted code as shown above
- Compile the solution
It doesn’t compile yet so next add the implementation.
public IList<IPerson> GetAll()
{
var tableAdapter =
new PersonDataSetTableAdapters.PersonTableAdapter();
var results = tableAdapter.GetData();
if (results == null || results.Count == 0)
{
return null;
}
else
{
var adapter = new PersonToDataSetAdapter();
var toValues = new List<IPerson>();
adapter.Adapt<T>(results, toValues);
return toValues;
}
}
- Go to SqlPersonRepository.cs
- Add the highlighted code as shown above
- Run the unit tests
Visual Studio 2010 Testing Labs Page 51 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The GetAllPersons test should now be passing.
Modify Person: Write & Run the Test Just one test left and we’ll have a complete Repository! This one will implement the case to modify an
existing record in the database.
[TestMethod]
public void ModifyPerson()
{
var instance = CreateAndSavePerson();
// reload the person to get a copy
IPerson clone =
RepositoryInstance.GetById(instance.Id);
// modify the original
instance.FirstName = UnitTestUtility.GetUniqueString();
instance.LastName = UnitTestUtility.GetUniqueString();
instance.EmailAddress = UnitTestUtility.GetUniqueString();
RepositoryInstance.Save(instance);
Assert.AreEqual<int>(clone.Id,
instance.Id,
"Id value should not change after saving a modification.");
AssertAreEqual(instance, RepositoryInstance.GetById(instance.Id));
}
- Go to SqlPersonRepositoryFixture.cs
- Add the highlighted code as shown above
Visual Studio 2010 Testing Labs Page 52 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Next, add the implementation.
public void Save(IPerson saveThis)
{
if (saveThis == null)
throw new ArgumentNullException("saveThis", "saveThis is null.");
if (saveThis.Id == 0)
{
Insert(saveThis);
}
else
{
Update(saveThis);
}
}
private void Update(IPerson saveThis)
{
var tableAdapter =
new PersonDataSetTableAdapters.PersonTableAdapter();
var dataset = new PersonDataSet();
tableAdapter.FillById(dataset.Person, saveThis.Id);
if (dataset.Person == null || dataset.Person.Count == 0)
{
throw new InvalidOperationException("Unknown person.");
}
else
{
var adapter = new PersonToDataSetAdapter();
adapter.Adapt(saveThis, dataset.Person[0], true);
Save(dataset);
}
}
- Go to SqlPersonRepository.cs
- Add the highlighted code as shown above
- Run all the tests
Visual Studio 2010 Testing Labs Page 53 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The tests are all passing and you’ve finished implementing the Repository pattern.
Visual Studio 2010 Testing Labs Page 54 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Lab 04: Test Logic in Isolation Using Mock Objects
Introduction
There’s a reason why they’re called unit tests. You’re trying to test small units of functionality. It’s easy
to say that you should test isolated units of functionality but a lot of times – most of the time, actually –
the code we want to test is pretty complex and often has dependencies on other classes. Getting our
test set up and ready to go can be a real pain sometimes because of all the little dependencies from
object to object.
A great way to keep focused on unit tests and isolated logic is using the Dependency Injection Pattern
(DI) and mock objects. Dependency Injection is a way of structuring your objects so that any
dependencies that they have on other objects are explicitly called out on their constructor. If a
PersonManager object requires an instance of IPersonRepository in order to save to a database, then
the constructor for the PersonManager object will require a non-null instance of IPersonRepository to
be supplied. So, rather than having PersonManager go out and get an instance of IPersonRepository
itself, that constructor now requires us to inject the dependency.
If you’re doing Dependency Injection, you’re typically using interface types on the constructors rather
than concrete types. For example, IPersonRepository rather than Oracle9iPersonRepository,
SqlPersonRepository, or XmlFileSystemPersonRepository. So, what does this have to do with unit
testing? Well, since PersonManager only cares that the repository implements IPersonRepository rather
than any particular concrete implementation, we can inject simplified, faked out versions of the
repository during our tests called Mocks.
These mock objects implement the required interfaces and provide simplified, canned answers to
method calls. Having a simple object that provides canned answers allows us to quickly and easily
simulate certain scenarios without having to spend a ton of time – for example – loading up a database
with the particular data in order to support the scenario that we need to test. Want to simulate a
duplicate record in the database? Have the mock repository report that there’s a duplicate record.
Want to simulate the saving of new record in the database? Have the mock repository simulate that
insert.
By using simplified mock objects, you can focus on testing only the code that you really care about.
Lab Overview
In this lab, you’ll work on testing a class named CustomerManager. The code you’ll work on in this lab is
focused on the generation of unique customer Account Codes. Our fictional company has a 6 character
account code that is generated for each new customer. This account code has to be unique for each
customer and it’s CustomerManager’s job to generate account codes and to make sure that nothing is
saved with a duplicate account code. In order to do this work, it relies on ICustomerRepository’s
IsAccountCodeInUse() method to determine if a code is unique.
Visual Studio 2010 Testing Labs Page 55 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
In this test, we simply don’t care how ICustomerRepository is implemented. We only care about the
logic that happens in CustomerManager itself. Therefore, there’s no need to have a real database-based
implementation of ICustomerRepository. Plus, some of the cases we need to test are fairly complex so
it’s actually easier to skip the database dependency altogether.
Brainstorming the Tests
CustomerManager needs to be able to generate a suggested account code. We’ll also need it to check if
an account code is in use. If the code is in use, we’ll need it to retry up to 10 times until it gets a unique
code. We’ll also have a CreateNewCustomer() method that will save the new customer to the
repository but before it does it’ll check that the code is unique. If the code is unique, it’ll save. If the
code is not unique, it’ll throw an exception.
That sounds like probably 6 tests:
1. CreateSuggestedCustomerAccountCode()
2. CreateCustomerWithUniqueAccountCodeSucceeds()
3. CreateAccountCodeSucceedsWhenCodeIsUnique()
4. CreateAccountCodeRetriesWhenOriginalIsInUse()
5. CreateAccountCodeRetriesUntilCodeIsFound()
6. CreateAccountCodeThrowsExceptionAfter9Tries()
Create a Mock ICustomerRepository
Let’s say that someone’s already done some work on CustomerManager and they’ve already
implemented enough so that there’s a constructor. The constructor requires a non-null instance of
ICustomerRepository.
The first test you need to work on is just a simple method to generate suggested Account Codes and
there’s no database access required to implement that. Unfortunately, there’s no way to create an
instance of CustomerManager unless you pass in that instance of ICustomerRepository.
Visual Studio 2010 Testing Labs Page 56 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
To make things worse, you don’t want to use the existing implementation of ICustomerRepository
because it’s got insane amounts of complexity to it and using it will add way too much complexity to
your test.
You need your own mock version of repository.
- Go to the Labs.Tests project
- Create a Class named MockCustomerRepository.cs
Visual Studio 2010 Testing Labs Page 57 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
public class MockCustomerRepository : ICustomerRepository
{
public MockCustomerRepository()
{
}
public bool IsAccountCodeInUse(string accountCode)
{
throw new NotImplementedException();
}
public void SaveCustomer(string accountCode, string customerName)
{
throw new NotImplementedException();
}
}
- Add the highlighted code to MockCustomerRepository as shown above
Now that this MockCustomerRepository exists, you have a class that you can pass in to the constructor
for CustomerManager. It doesn’t do anything but it will satisfy the requirements of the constructor for
the purpose of our first test.
Create Suggested Account Code: Write the Test and Implementation
First write the test.
[TestMethod]
public void CreateSuggestedCustomerAccountCode()
{
var manager = new CustomerManager(
new MockCustomerRepository());
string result = manager.GetSuggestedAccountCode();
Assert.IsFalse(String.IsNullOrWhiteSpace(result),
"Result should not be null or whitespace.");
Assert.AreEqual<int>(6, result.Trim().Length,
"Account code should be 6 characters long.");
}
- Open to CustomerManagerFixture.cs
- Add the highlighted code to the CreateSuggestedCustomerAccountCode() test as shown above
Visual Studio 2010 Testing Labs Page 58 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Next, implement the GetSuggestedAccountCode() method on CustomerManager.
public class CustomerManager
{
public CustomerManager(ICustomerRepository instance)
{
if (instance == null)
throw new ArgumentNullException("instance",
"instance is null.");
m_RepositoryInstance = instance;
}
public string GetSuggestedAccountCode()
{
string guid = Guid.NewGuid().ToString();
return guid.Substring(0, 6);
}
private ICustomerRepository m_RepositoryInstance;
private ICustomerRepository RepositoryInstance
{
get { return m_RepositoryInstance; }
}
}
- Open CustomerManager.cs
- Add the highlighted code as shown above
- Run the unit tests
The CreateSuggestedCustomerAccountCode test should be passing.
Visual Studio 2010 Testing Labs Page 59 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Create Account Code: Write the Test
That first test was pretty easy. It simply tested the logic to generate account code values. Next it’s time
to write a test that will test the process of getting a suggested account code, verifying that it is unique,
and saving it to the repository.
[TestMethod]
public void CreateAccountCodeSucceedsWhenCodeIsUnique()
{
var mockRepository = new MockCustomerRepository();
var manager = new CustomerManager(mockRepository);
string result = manager.CreateAccountCode("Customer 123");
Assert.IsFalse(String.IsNullOrWhiteSpace(result),
"Result should not be null or whitespace.");
mockRepository.AssertSaveCustomerWasCalled();
mockRepository.AssertIsAccountCodeInUseWasCalled();
}
- Add the highlighted test code as shown above
Notice those last two lines of the test. We’re going to use the MockCustomerRepository to verify that
the CustomerManager made the calls that we expected.
- Compile the code
Visual Studio 2010 Testing Labs Page 60 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Create Account Code: Write Enough Code to Compile
The code compile should be failing right now so now you’ll add the implementation to
MockCustomerRepository and to CustomerManager.
using System;
using System.Collections.Generic;
using System.Linq;
using Labs.Interfaces;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Labs.Tests
{
public class MockCustomerRepository : ICustomerRepository
{
public MockCustomerRepository()
{
}
private bool m_WasIsAccountCodeInUseCalled = false;
public bool IsAccountCodeInUse(string accountCode)
{
m_WasIsAccountCodeInUseCalled = true;
return false;
}
private bool m_WasSaveCustomerCalled = false;
public void SaveCustomer(string accountCode, string customerName)
{
m_WasSaveCustomerCalled = true;
}
public void AssertSaveCustomerWasCalled()
{
Assert.IsTrue(m_WasSaveCustomerCalled,
"SaveCustomer() was not called.");
}
public void AssertIsAccountCodeInUseWasCalled()
{
Assert.IsTrue(m_WasIsAccountCodeInUseCalled,
"IsAccountCodeInUse() was not called.");
}
}
}
- Add the highlighted code to MockCustomerRepository.cs as shown above
Visual Studio 2010 Testing Labs Page 61 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Next let’s add just enough code to compile to CustomerManager.
public class CustomerManager
{
public CustomerManager(ICustomerRepository instance)
{
if (instance == null)
throw new ArgumentNullException("instance",
"instance is null.");
m_RepositoryInstance = instance;
}
public string CreateAccountCode(string customerName)
{
return null;
}
public string GetSuggestedAccountCode()
{
string guid = Guid.NewGuid().ToString();
return guid.Substring(0, 6);
}
private ICustomerRepository m_RepositoryInstance;
private ICustomerRepository RepositoryInstance
{
get { return m_RepositoryInstance; }
}
}
- Add the highlighted code to CustomerManager.cs as shown above
- Compile the code
- Run the tests
Visual Studio 2010 Testing Labs Page 62 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The CreateAccountCodeSucceedsWhenCodeIsUnique test is failing because the current implementation
returns a null Account Code.
Create Account Code: Write Enough to Make the Test Pass
Now let’s implement the code to make this unit test pass.
public string CreateAccountCode(string customerName)
{
if (String.IsNullOrEmpty(customerName) == true)
throw new ArgumentException(
"customerName is null or empty.", "customerName");
var suggestedAccountCode = GetSuggestedAccountCode();
if (RepositoryInstance.IsAccountCodeInUse(
suggestedAccountCode) == true)
{
throw new InvalidOperationException("Duplicate account code.");
}
else
{
RepositoryInstance.SaveCustomer(
suggestedAccountCode, customerName);
return suggestedAccountCode;
}
}
- Add the highlighted code to CustomerManager.cs as shown above
- Compile the code
- Run the tests
Visual Studio 2010 Testing Labs Page 63 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
This test now passes.
Retry Logic: Write the Test The last two tests start to get a little more intricate because of the “retry” logic. Fortunately, the
MockCustomerRepository class will help us to keep the logic organized and keep the test code from
getting overly complex.
One thing we’ll need is a way to easily get the repository to say that an Account Code is already taken.
We also need to check that the CustomerManager is generating new AccountCodes each time it checks
for uniqueness. Finally, we’ll need a way to verify that the retry logic is triggered 2 times.
[TestMethod]
public void CreateAccountCodeRetriesWhenOriginalIsInUse()
{
var mockRepository = new MockCustomerRepository();
// number of attempts required to get a unique code
mockRepository.NumberOfRequiredIsAccountCodeInUseAttempts = 2;
var manager = new CustomerManager(mockRepository);
string result = manager.CreateAccountCode("Customer 123");
Assert.IsFalse(String.IsNullOrWhiteSpace(result),
"Result should not be null or whitespace.");
mockRepository.AssertSaveCustomerWasCalled();
// check that it was called twice
mockRepository.AssertIsAccountCodeInUseWasCalled(2);
mockRepository.AssertReceivedAccountCodesWereDifferent();
}
- Add the highlighted test code as shown above
Visual Studio 2010 Testing Labs Page 64 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
There are a handful of things that we’re adding to this test.
1. We’re adding logic to the MockCustomerRepository to simulate IsAccountCodeInUse() results
using the NumberOfRequiredIsAccountCodeInUseAttempts property. Basically, we want the
MockCustomerRepository to report that an Account Code is in use until it has received X
number of attempts. We don’t really care about whether they’re really unique because we’re
only looking for the repository to *emulate* the behavior.
2. We’re adding an overload of the AssertIsAccounCodeInUseWasCalled() method that takes an
integer. This argument will allow us to ask the repository if it was called the right number of
times. In this test, we want the first check to say that the code is in use and then verify that the
retry logic was called for a second (successful) attempt.
3. The AssertReceivedAccountCodesWereDifferent() method will check that the account codes
that were checked for uniqueness changed with each attempt.
So, the [TestMethod] method is staying fairly simple but we’re adding some test-specific functionality to
the repository.
Visual Studio 2010 Testing Labs Page 65 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Retry Logic: Implement the Changes to MockCustomerRepository
Let’s implement the MockCustomerRepository changes.
public class MockCustomerRepository : ICustomerRepository
{
public MockCustomerRepository()
{
}
private bool m_WasIsAccountCodeInUseCalled = false;
private List<string> m_IsAccountCodeInUseAttemptValues =
new List<string>();
public bool IsAccountCodeInUse(string accountCode)
{
m_IsAccountCodeInUseAttemptValues.Add(accountCode);
m_WasIsAccountCodeInUseCalled = true;
if (NumberOfRequiredIsAccountCodeInUseAttempts == 0 ||
m_IsAccountCodeInUseAttemptValues.Count >=
NumberOfRequiredIsAccountCodeInUseAttempts)
{
return false;
}
else
{
return true;
}
}
public int NumberOfRequiredIsAccountCodeInUseAttempts { get; set; }
public void AssertIsAccountCodeInUseWasCalled(int expectedCount)
{
Assert.AreEqual<int>(expectedCount,
m_IsAccountCodeInUseAttemptValues.Count,
"Method was not called the expected number of times.");
}
public void AssertReceivedAccountCodesWereDifferent()
{
CollectionAssert.AllItemsAreUnique(
m_IsAccountCodeInUseAttemptValues,
"IsAccountCodeInUse values should have been unique.");
}
private bool m_WasSaveCustomerCalled = false;
public void SaveCustomer(string accountCode, string customerName)
{
...
- Add the highlighted code to MockCustomerRepository.cs as shown above
- Run the tests
Visual Studio 2010 Testing Labs Page 66 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The CreateAccountCodeRetriesWhenOriginalIsInUse() test should be failing and the previous two tests
you worked on should still be passing. The fact that the previous two tests are still passing is very
important because the changes you just made to the MockCustomerRepository changed the logic of the
previous tests a little bit.
The error message that you should see for CreateAccountCodeRetriesWhenOriginalIsInUse should say
System.InvalidOperationException: Duplicate account code. This is happening because the current
logic simply throws an exception when it finds a non-unique account code. Now, we could have just
written the retry logic into the CustomerManager from the beginning but this would have violated Test-
Driven Development because we wouldn’t have any test for this logic. Now that we have the test for
this retry logic, we can proceed with the implementation.
Visual Studio 2010 Testing Labs Page 67 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Retry Logic: Write Enough to Make the Test Pass
Asdf
public string CreateAccountCode(string customerName)
{
if (String.IsNullOrEmpty(customerName) == true)
throw new ArgumentException(
"customerName is null or empty.", "customerName");
bool isValid = false;
var suggestedAccountCode = GetSuggestedAccountCode();
if (RepositoryInstance.IsAccountCodeInUse(
suggestedAccountCode) == true)
{
suggestedAccountCode = GetSuggestedAccountCode();
if (RepositoryInstance.IsAccountCodeInUse(
suggestedAccountCode) == false)
{
isValid = true;
}
}
else
{
isValid = true;
}
if (isValid == true)
{
RepositoryInstance.SaveCustomer(
suggestedAccountCode, customerName);
return suggestedAccountCode;
}
else
{
throw new InvalidOperationException("Duplicate account code.");
}
}
- Open CustomerManager.cs
- Modify the CreateAccountCode(string customerName) method with the highlighted changes
- Run the tests
Visual Studio 2010 Testing Labs Page 68 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The CreateAccountCodeRetriesWhenOriginalIsInUse test should be passing.
Visual Studio 2010 Testing Labs Page 69 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Multiple Retry Logic: Write the Test
Only one test left to do. We’ve got the retry logic done but there’s still the case of multiple required
retry attempts.
From the perspective of the test code, there really isn’t much difference between running the test for 2
required IsAccountCodeInUse attempts and 10 attempts . So what we really need to do here is, extract
the test code from CreateAccountCodeRetriesWhenOriginalIsInUse() into a re-usable method (see code
sample below). This is what is known as an Extract Method refactoring.
There are two ways to do this:
1. Do it by hand
2. Use the Visual Studio 2010 Refactoring menu
To use the Visual Studio refactoring, you’ll select the contents of the
CreateAccountCodeRetriesWhenOriginalIsInUse method, go to the Refactor menu, and then choose
Extract Method.
Visual Studio 2010 Testing Labs Page 70 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
However you choose to do it, when you’re done, the code should look like the sample below.
[TestMethod]
public void CreateAccountCodeRetriesWhenOriginalIsInUse()
{
RunAndAssertIsAccountCodeInUseForNumberOfAttempts(2);
}
[TestMethod]
public void CreateAccountCodeRetriesUntilUniqueCodeIsFound()
{
RunAndAssertIsAccountCodeInUseForNumberOfAttempts(10);
}
private void RunAndAssertIsAccountCodeInUseForNumberOfAttempts(
int attemptCount)
{
var mockRepository = new MockCustomerRepository();
// number of attempts required to get a unique code
mockRepository.NumberOfRequiredIsAccountCodeInUseAttempts =
attemptCount;
var manager = new CustomerManager(mockRepository);
string result = manager.CreateAccountCode("Customer 123");
Assert.IsFalse(String.IsNullOrWhiteSpace(result),
"Result should not be null or whitespace.");
mockRepository.AssertSaveCustomerWasCalled();
// check that it was called twice
mockRepository.AssertIsAccountCodeInUseWasCalled(attemptCount);
mockRepository.AssertReceivedAccountCodesWereDifferent();
}
- Modify the code to look like the code above. (NOTE: don’t forget to change the code to use the
attemptCount variables!)
- Run the tests
Visual Studio 2010 Testing Labs Page 71 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The CreateAccountCodeRetriesUntilCodeIsFound test is failing with a Duplicate account code error
message.
This is happening because we haven’t written any ‘multiple retry’ code and currently have only single-
retry code.
Visual Studio 2010 Testing Labs Page 72 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Multiple Retry Logic: Write Enough to Make the Test Pass
Almost done. Just a few changes to CustomerManager to go.
public string CreateAccountCode(string customerName)
{
if (String.IsNullOrEmpty(customerName) == true)
throw new ArgumentException(
"customerName is null or empty.", "customerName");
bool isValid = false;
string suggestedAccountCode = null;
while (isValid == false)
{
suggestedAccountCode = GetSuggestedAccountCode();
if (RepositoryInstance.IsAccountCodeInUse(
suggestedAccountCode) == false)
{
isValid = true;
}
}
if (isValid == true)
{
RepositoryInstance.SaveCustomer(
suggestedAccountCode, customerName);
return suggestedAccountCode;
}
else
{
throw new InvalidOperationException("Duplicate account code.");
}
}
- Modify the CreateAccountCode() method with the highlighted code as shown above
- Run the tests
Visual Studio 2010 Testing Labs Page 73 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
All the tests should now be passing.
You’ve just implemented a suite of features in the CustomerManager class without needing a database
and without requiring any database setup logic.
Visual Studio 2010 Testing Labs Page 74 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Lab 05: Unit Test a User Interface Using the Model-View-Presenter Pattern
Prerequisite
This lab uses the SQL Server Northwind sample database.
1. Create a directory on your machine at C:\code\labs\northwind
2. Place a copy of the Northwind database files into this directory
Introduction Up to now, we’ve been living in the world of basic unit testing. When you’re developing a multi-tier
application, it’s pretty important in order to ensure quality but it can be pretty abstract.
What about something a little more “real”? Users aren’t going to be interacting with the application
through your unit tests. How would you test a screen in a user interface?
Well, if you’re writing a web application, Visual Studio Ultimate allows you to write Web Tests or Coded
UI tests that will exercise your UI. Unfortunately, both of these are fairly high-level and don’t give you
the granularity to really dive in and do serious testing.
Let’s face it – unit testing a user interface is difficult. User interfaces are built for users and users are
human. We’re good at locating things on the screen and figuring out what’s going on. Computers are
powerful but dumb. In order to let them unit test our UIs, we need to spell everything out simply and
clearly.
The real answer is to design your user interfaces for testability and one of the best ways to do this is
using a user interface interaction design pattern like Model-View-Presenter (MVP). MVP is a variation
of the Model-View-Controller Pattern. MVP is particularly well suited to modeling user interfaces that
will be implemented using ASP.NET Web Forms or Windows Forms.
The MVP consists of 3 parts:
1) The Model which represents your business objects and/or data.
2) The View which is an abstraction of your user interface screen.
3) The Presenter which contains all the logic for the various actions your View needs to perform
and is responsible for marshaling database between the View and the Model.
The really key concept is that your user interface (ASP.NET, Windows Form, etc) will have practically no
code in the “code behind” other than a few simple calls to talk to the Presenter. Since the Presenter will
have no knowledge of how the Views are actually implemented, they’re very easy to test using ordinary
unit tests.
Visual Studio 2010 Testing Labs Page 75 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Requirements
A lot of times, when you need to develop a user interface, a graphic designer or someone else will hand
you a picture of what they want the screen to look like. This is a good place to figure out what your
screen has to do and what all the values and operations are that will need to be tested.
The image below represents the screen mockup that’s just been handed to us to develop. It’s a search
screen for the company database.
The user will type in a company name search in the search box, press the Search button and display the
results.
In order to accomplish this, there are only 2 pieces of data that get passed around: the search string, and
the DataTable containing the results.
Visual Studio 2010 Testing Labs Page 76 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The Unit Tests & Code Structure
In order to meet these requirements, you going to need a [TestClass] called
CustomerSearchPresenterFixture and four tests:
1) CheckInitialize() – to verify that the screen is properly initialized at startup
2) SearchForUnknownValueReturnsNoResults()
3) SearchForKnownRecordsReturnsResults()
4) SearchUsingEmptyStringReturnsNoResults()
Visual Studio 2010 Testing Labs Page 77 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The implementation will go into the Labs.Business project and will use ICustomerSearchView to
represent the view and CustomerSearchPresenter to contain the logic for the screen.
The final steps of this lab will walk you through implementing the ASP.NET version of this functionality in
the Labs.WebUI project.
Visual Studio 2010 Testing Labs Page 78 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Write the Tests
Let’s write the code for the tests in CustomerSearchPresenterFixture.cs.
- Open CustomerSearchPresenterFixture.cs
First the CheckInitialize() method.
[TestMethod]
public void CheckInitialize()
{
ICustomerSearchView view = new CustomerSearchViewStub();
var presenter = new CustomerSearchPresenter();
presenter.Initialize(view);
Assert.AreEqual<string>(String.Empty, view.SearchValue,
"SearchValue was not empty.");
Assert.IsNull(view.Results, "Results should be null.");
}
- Add the highlighted implementation as shown above
Next the SearchForUnknownValueReturnsNoResults() method.
[TestMethod]
public void SearchForUnknownValueReturnsNoResults()
{
ICustomerSearchView view = new CustomerSearchViewStub();
var presenter = new CustomerSearchPresenter();
presenter.Initialize(view);
// search for something that doesn't exist
view.SearchValue = Guid.NewGuid().ToString();
presenter.Search(view);
Assert.IsNotNull(view.Results, "Results was null.");
Assert.AreEqual<int>(0, view.Results.Count,
"Row count was wrong.");
}
- Add the highlighted implementation as shown above
Visual Studio 2010 Testing Labs Page 79 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The SearchForKnownRecordsReturnsResults() method.
[TestMethod]
public void SearchForKnownRecordsReturnsResults()
{
ICustomerSearchView view = new CustomerSearchViewStub();
var presenter = new CustomerSearchPresenter();
presenter.Initialize(view);
// search for known records
view.SearchValue = "super";
presenter.Search(view);
Assert.IsNotNull(view.Results, "Results was null.");
Assert.AreEqual<int>(2, view.Results.Count,
"Row count was wrong.");
}
- Add the highlighted implementation as shown above
And finally, the SearchUsingEmptyStringReturnsNoResults() method.
[TestMethod]
public void SearchUsingEmptyStringReturnsNoResults()
{
ICustomerSearchView view = new CustomerSearchViewStub();
var presenter = new CustomerSearchPresenter();
presenter.Initialize(view);
// empty search should return no records
view.SearchValue = "";
presenter.Search(view);
Assert.IsNotNull(view.Results, "Results was null.");
Assert.AreEqual<int>(0, view.Results.Count,
"Row count was wrong.");
}
- Add the highlighted implementation as shown above
Now that you’ve entered the test code it’s time to try to compile.
- Compile the code
Visual Studio 2010 Testing Labs Page 80 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The build succeeded. If you’ve done all the labs to date, I’ll be you didn’t expect that to happen.
The enough code has already been created in order to make the projects compile.
- Run the unit tests
All the tests should be failing.
Visual Studio 2010 Testing Labs Page 81 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Implement the CustomerSearchPresenter Code
At the moment, CustomerSearchPresenter has no implementation and that’s why the tests are failing.
First, implement the code for the Initialize() method.
public void Initialize(ICustomerSearchView view)
{
if (view == null)
{
throw new ArgumentNullException(
"search", "Argument cannot be null.");
}
view.SearchValue = String.Empty;
view.Results = null;
}
- Open CustomerSearchPresenter.cs
- Add the highlighted code as shown above
Visual Studio 2010 Testing Labs Page 82 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Next, implement the Search() method.
public void Search(ICustomerSearchView view)
{
if (view == null)
{
throw new ArgumentNullException("search",
"Argument cannot be null.");
}
if (String.IsNullOrEmpty(view.SearchValue) == true)
{
// empty result set
view.Results =
new NorthwindDataset.CustomersDataTable();
}
else
{
CustomersTableAdapter tableAdapter =
new CustomersTableAdapter();
view.Results =
tableAdapter.GetDataByCustomerName(
String.Format("%{0}%", view.SearchValue));
}
}
- Add the highlighted code as shown above
Visual Studio 2010 Testing Labs Page 83 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Run the Tests
Now that the tests and the implementation are there, compile and run the tests.
- Compile the code
- Run the tests
All the tests should pass.
Implement the User Interface
Now that we have working and tested code to build upon, we can go ahead and implement our real user
interface using ASP.NET.
- Go to the Labs.WebUI project
- Open CustomerSearch.aspx.cs
Visual Studio 2010 Testing Labs Page 84 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
The there’s no logic here but the basic structure that we’ll need for the page are already here. Pretty
much all you have to do is make this page implement the ICustomerSearchView interface.
Visual Studio 2010 Testing Labs Page 85 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
public partial class CustomerSearch :
System.Web.UI.Page, ICustomerSearchView
{
private CustomerSearchPresenter m_Presenter =
new CustomerSearchPresenter();
protected void Page_Load(object sender, EventArgs e)
{
if (this.IsPostBack == false)
{
m_Presenter.Initialize(this);
}
}
public string SearchValue
{
get
{
return m_SearchCompanyName.Text;
}
set
{
m_SearchCompanyName.Text = value;
}
}
public NorthwindDataset.CustomersDataTable Results
{
get
{
return m_Grid.DataSource as NorthwindDataset.CustomersDataTable;
}
set
{
m_Grid.DataSource = value;
m_Grid.DataBind();
}
}
protected void m_BtnSearch_Click(object sender, EventArgs e)
{
m_Presenter.Search(this);
}
}
- Add the highlighted code as shown above to CustomerSearch.aspx.cs
- Compile the code
The code should compile.
Visual Studio 2010 Testing Labs Page 86 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Run the Web Application
Now it’s time to run the application.
If the text for the Labs.WebUI project is not in bold, you need to make it the StartUp Project for the
solution.
- Go to Solution Explorer
- Right-click the Labs.WebUI project
- Choose Set as StartUp Project from the context menu
Visual Studio 2010 Testing Labs Page 87 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
Now run the application.
- Go to the Debug menu
- Choose Start Debugging
You should see a browser window and the application should appear.
- In the Enter all or part of a company name box, type rest
- Click the Search button
Visual Studio 2010 Testing Labs Page 88 of 88
Copyright © 2011 – Benjamin Day Consulting, Inc. – www.benday.com
Printing or duplication is prohibited without author’s expressed written permission.
You should see the matching results in the Search Results grid.
You’ve just implemented your first testable user interface using the Model View Presenter Pattern.