Adherents for a “Test First!” methodology make a strong case for the importance of writing unit tests as an integral part of your application development process – and one should be commenced early in the development cycle. Let’s see how to lay the foundation for integrated, ongoing, automated testing and test development.
You’ll need Team Test to reproduce the steps detailed in this chapter.
Do you have Team Test? Look for “Test” among the Visual Studio menus. If it’s there, you’ve got Team Test; if it isn’t, you don’t.
If you don’t wish to purchase an edition of Visual Studio that has Team Test, you might consider NUnit. It’s solid and it’s free. While it won’t generate the template tests (we’ll see Team Test do that shortly), you’ll otherwise find the experience of using it remarkably similar to testing with Team Test.
You know you need to write unit tests for your application, but you don't know where to start.
Here we'll walk through how to add a test project to your solution and write some simple tests.
It’s easy to get started with the new Visual Studio Team Test.
The “Create Unit Tests” Dialog appears. Uncheck all methods except the MainWindowViewModel constructor:
After some grinding in the background, you will see the Test project added to the solution, with references to the DomainModel and the necessary IdeaBlade assemblies. You will also see a new MainWindowViewModelTest.cs (or .vb) tab:
We need to make a couple of configuration changes to ready our test project for use.
C# | using WpfApplication1; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.Linq; using IdeaBlade.Core; using IdeaBlade.EntityModel; using DomainModel; |
VB | Imports WpfApplication1 Imports Microsoft.VisualStudio.TestTools.UnitTesting Imports System Imports System.Collections.Generic Imports System.Linq Imports IdeaBlade.Core Imports IdeaBlade.EntityModel Imports DomainModel |
Your test environment is configured, and you’re ready to run a test.
This will open a tab in the group of tabbed panels at the right of the Visual Studio development environment. Find the test named “MainWindowViewModelConstructorTest”. Right-click it, select Run Selection, and note the results shown below:
This corresponds to the following test method which was generated automatically into the MainWindowViewModelTest class:
C# | /// <summary> ///A test for MainWindowViewModel Constructor ///</summary> [TestMethod()] public void MainWindowViewModelConstructorTest() { MainWindowViewModel target = new MainWindowViewModel(); Assert.Inconclusive("TODO: Implement code to verify target"); } |
VB | ''' <summary> '''A test for MainWindowViewModel Constructor '''</summary> <TestMethod()> _ Public Sub MainWindowViewModelConstructorTest() Dim target As New MainWindowViewModel() Assert.Inconclusive("TODO: Implement code to verify target") End Sub |
The test method does indeed construct an instance of MainWindowViewModel, but the Assert call that produces test results doesn’t yet do anything meaningful. Since the MainWindowViewModel’s constructor doesn’t do anything except the bare minimum, about all we can verify is the value of the target variable isn’t null. We can change our Assert statement as follows:
C# | Assert.IsTrue(target | null, "Unable to construct MainWindowViewModel"); |
VB | Assert.IsTrue(target Or Nothing, "Unable to construct MainWindowViewModel") |
The second parameter to the Assert.IsTrue() true is the Error Message that would display in the Test Results panel if the test were ever to fail. Of course, there’s not much likelihood of that, so let’s write a more useful test.
C# | ///A test for the Customer query used in ///MainWindowViewModel.FirstSample() ///</summary> [TestMethod()] public void CustomerQueryTest() { NorthwindIBEntityManager mgr = NorthwindIBEntityManager.DefaultManager; var customersQuery = from cust in mgr.Customers where cust.ContactTitle == "Sales Representative" orderby cust.CompanyName select cust; Assert.IsTrue(customersQuery.Count() > 0, "Customer query returned no Customers"); } |
VB | '''A test for the Customer query used in '''MainWindowViewModel.FirstSample() '''</summary> <TestMethod()> _ Public Sub CustomerQueryTest() Dim mgr As NorthwindIBEntityManager = _ NorthwindIBEntityManager.DefaultManager Dim customersQuery = From cust In mgr.Customers Where cust.ContactTitle = _ "Sales Representative" Order By cust.CompanyName Select cust Assert.IsTrue(customersQuery.Count() > 0, _ "Customer query returned no Customers") End Sub |
This new test method tests the query used in the FirstSample() method in the MainWindowViewModel. At this point it might be a good idea to refactor FirstSample() to separate the code that defines this query, so that our test method would be guaranteed to test exactly the same query used by FirstSample(). But we’ll leave that as an exercise for you.
Suppose your test did not pass. There are two reasons a test will fail:
If an Assert statement fails, then in the Error Message column of the Test Results you’ll see the message you encoded into the call -- e.g., “Customer query returned no Customers” in our Assert.IsTrue() call.
If the test failed because an exception was thrown, you see a generalized message about the test failing. To get more information, you can right-click on that message and select View Test Result Details, as shown here.
Below, as an example, is the error you get if the connection string that the Test engine found in the supplied config file was bad:
When designing tests you’ll want to be careful to keep the test data always in a known state. If your test code makes changes, you’ll probably need some process to restore the original data. You should have a back up of the database just in case.
One of the cardinal testing rules is that there should be no dependencies among the tests. That means that tests can be run independently and in any order. Accordingly, if you make changes during a test, you must restore the original data immediately after the test. You must make sure we do so even if the test fails.
Do restore the test data source after every test
Do maintain a back up of the database just in case.
We launched the tests we’ve run herein using Run Selection. You can also start a test using Debug Selection:
Starting your test with Debug Selection will cause any breakpoints you place in the test code to be respected. That can be very helpful while writing a test to help you decide exactly what to compare in your Assert statements, and later to see why a particular test is failing.
Team Test keeps track of every test run. You can review them from within the “Test Results” window shown immediately above.
When you examine your solution directory in Windows Explorer, you will find the TestResults directory where these results are stored. You don’t want this directory under source control and you definitely want to clear it out periodically. Prune or delete at your leisure.