Skip Navigation LinksHome > SSW Standards > Rules > SSW Rules to Better Unit Tests

I've been putting together Development Guidelines for my employer and in the process have reviewed many published standards (in the .Net arena) from around the world. In each category, the suggestions at SSW are always among the best. Leon Bambrick -
 

Do you agree with them all? Are we missing some? Email me your tips, thoughts or arguments. Let me know what you think.

Red star Indicates important rule

  1. Do you know why tests are important?Red star
  2. What are the different types of test you can have?
  3. How do you structure a unit test (aka the 3 a's)?
  4. Do you know the few important documents to read to get you started on Unit Testing?
  5. Do you know what unit tests to write and how many?Red star
  6. Do you reply "DONE + Added a unit test so it can't happen again"?
  7. Do you follow the standard naming conventions for Tests?Red star
  8. Do you know when you are adding a unit test for an edge case - the naming convention should just be the issue ID?
  9. Do you have unit tests outside the project - not inside?Red star
  10. Do you write Integration Test for Dependencies - e.g. DLLs?Red star
  11. Do you *not* write integration tests for Database Dependencies - e.g. Database Schema, Datasets?Red star
  12. Do you write integration tests for Web Service Dependencies?Red star
  13. Do you write integration tests for your database configuration?
  14. Do you write integration tests for your reporting service configuration?
  15. Do you write integration tests to validate your web links?
  16. Do you test your JavaScript?
  17. Do you test Fragile Code - e.g. Regular Expressions?Red star
  18. Do you have tests for difficult to spot errors - e.g. Arithmetic, Rounding, Calculations?Red star
  19. Do you have tests for Performance?Red star
  20. Do you run Unit Tests in Visual Studio?Red star
  21. Do you have a integration test for your send mail code?
  22. Do you have a /HealthCheck (was /zsValidate) page to test your website dependencies?
  23. Do you know when functions are too complicated?
  24. Do you isolate your logic and remove dependencies on instances of objects?
  25. Do you isolate your logic from your IO to increase the testability?
  26. Do you make sure you integration tests include UI (aka form) testing?
  27. Do you know the best unit test framework to use for your project?
  28. Do you know how to run nUnit tests from within Visual Studio?
  29. Do you use the right version and configuration for Nunit?
  30. Did you know Visual Studio Team System does the same thing (but we don't recommend it)?
  31. Do you have a standard 'Help' menu that includes a way to run your unit tests?
  32. Do you make sure that the test can be failed?
  33. Do you have a Continuous Build Server?
  34. Do you unit test your database?
  35. When you record VS 2010 coded UI tests, make them less brittle
  1. Do you know why tests are important? Red star

    Customers get cranky when developers make a change, and this causes a bug to pop up somewhere else. They may also complain that they shouldn't have to pay for this new bug to be fixed. Although this is understandable, fixing bugs is a large part of the project and is always billable, although if these bugs can be caught early, they are quicker and cheaper to fix.

    When writing code, one of the most time consuming and frustrating parts of the process is finding that something you have changed has broken something you're not looking at. These bugs often don't get found for a long time as they are not the focus of the "test please" and only get found later by users.

    The best way to avoid this issue is to use Tests. These double check your core logic is still working every time you compile, and will minimise the number of bugs that get through your internal testing and end up being found by the client.

    This is a "Pay now -pay much less later" approach.

    More reasons for writing tests can be found on Peter's blog.

  2. What are the different types of test you can have?

      Smoke test
    • You fire up your application and click around prior to giving it to a tester. Most developers do this.
      Unit Tests
    • They are coded by a developer
    • Quick
    • Independent
    • Test just 1 method in a class
    • Tip: Use mock objects to make it faster and not to be bothered by external dependencies eg. the web service going down. The popular ones are: MOQ, TypeMock, NMock, RhinoMocks, nSubstitute
      Integrations Tests
    • They are coded by a developer
    • Slower
    • Test the interaction of components eg. Databases, Web Services
      Functional Tests
    • Recorded by a tester
    • Verifies the functionality of a system, typically from an end user perspective
    • Tip: Use VS Webtest if you have got no AJAX in your page
    • Tip: Use Coded UI Test or Telerik Tests to test the interactivity
      Load Tests
    • Setup by developers
    • Reuse the web tests
    • Simulate expected load on your application
    • Tip: Use VS Load tests or 3rd party such as loadstorm
    • Use the performance stats as a baseline for regression. You don't want to decrease performance in your application.
      Stress Tests
    • Setup by developers
    • Reuse web tests
    • Hit your application very hard, and try to see where your limits are (CPU, Network, Memory, ?
  3. How do you structure a unit test (aka the 3 a's)?

    A test verifies expectations. Traditionally it has the form of 3 major steps: 1. Arrange, 2. Act, 3. Assert.
    In the "Arrange" step we get everything ready and make sure we have all things handy for the "Act" step.
    The "Act" step executes the relevant code piece that we want to test.
    The "Assert" step verifies our expectation by stating what we were expecting from the system under test.
    Developers call this the "AAA" syntax.

    [TestMethod]
    public void TestRegisterPost_ValidUser_ReturnsRedirect()
    {
       // Arrange
       AccountController controller = GetAccountController();
       RegisterModel model = new RegisterModel()
       {
          UserName = "someUser",
          Email = "goodEmail",
          Password = "goodPassword",
          ConfirmPassword = "goodPassword"
       };
    
       // Act
       ActionResult result = controller.Register(model);
    
       // Assert
       RedirectToRouteResult redirectResult = (RedirectToRouteResult)result;
       Assert.AreEqual("Home", redirectResult.RouteValues["controller"]);
       Assert.AreEqual("Index", redirectResult.RouteValues["action"]);
    
    }
                                
    Figure: A good structure for a unit test
  4. Do you know the few important documents to read to get you started on Unit Testing?

    Do you know the basic readings to get an understanding of quality development

  5. Do you know what unit tests to write and how many?Red star

    People aim for 100% Unit Test Coverage but in the real world this is 100% impractical. Actually it seems that the most popular metric in TDD (Test Driven Development) is to aim for 100% of methods to be unit tested. However in the real world this goal is rarely, if ever, achieved. Unit tests are created to validate and assert that public and protected methods of a class meet an expected outcome based on varying input. This includes both good and bad data being tested, to ensure the method behaves as expected and returns the correct result or traps any errors.

    Generally, private methods should not have unit tests written for them as they are not exposed to other objects outside the original class. These private methods are likely to be refactored (eg. changed, renamed) over time and will require the unit tests to be updated and this becomes a maintenance nightmare. So how do private methods get tested? Private methods should be tested by the unit tests on the public and protected methods calling them and this will indirectly test the private method behaves as intended.

    E.g. You would test correct input such as 12/3 = 4 plus bad input such as 12/4 <> 4 and that 12/0 does not crash the application, and instead a DivideByZero Exception is thrown and handled gracefully.
    E.g. Methods returning a Boolean value need to have both true and false test cases.

    Unit tests should be written for:

    • Fragile Code - e.g. Regular Expressions - see below
    • When errors can be difficult to spot - e.g. Rounding, arithmetic, calculations - see below

    Unit tests should not be written for:

    • Dependencies - e.g. DLLs Run time errors (JIT) - see below
    • Dependencies - e.g. Database Schema, Datasets, Web Services - see below
    • Performance - e.g. Slow forms, Time critical applications - see below
    • When code has been generated from Code Generators eg. SQL database functions (Customer.Select, Customer.Update, Customer. Insert, Customer. Delete)
    • When unit tests become bigger than the original function eg. When you know to insert items into a database in the SetUp to test a function that uses the database
    • For Private methods because these will be tested by the public functions calling them, and they are likely to be change or refactored.
  6. Do you reply "DONE + Added a unit test so it can't happen again"?

    When you encounter a bug in your application you should never let the same bug happen again. The best way to do this is to write a unit test for the bug.
    See our Rules to Better Email to implement a good 'DONE' email.

  7. Do you follow the standard naming conventions for Tests?Red star

    Hi Adam

    As well as keeping your code tidy, using this naming convention also allows you to use TestDriven.Net's 'Go To Test/Code' command.
    This navigates between your tests and code under test (and back). This is something that test driven developers end up doing a lot.
    Screen captures at http://weblogs.asp.net/nunitaddin/archive/2010/06/09/testdriven-net-3-0-all-systems-go.aspx

    Jamie Cansdale
    --
    http://www.testdriven.net
    http://twitter.com/jcansdale

    Test Object Recommended Style Example
    Project Name Tests.[Testtypes].Projectname Tests.Unit.Common,Tests.Unit.WebFrontend,Test.Integration.MainWCFService
    Tests.Functional.SilverlightUI, Tests.Functional.WebUI *
    Test Fixture Name [Type]Tests OrdersTests, CustomerTests, DeveloperTests
    Test Case [Function]Test NullableIntTryParse_NumberIsValid1_Return1, StringHelperEncodeTo64_EncodeAndUnencodeString_ReturnSameString
    Set Up SetUp  
    Tear Down TearDown  

    *Test types are categorized into "Unit" "Integration" or "Functional" tests, as explained in "2. What are the different types of test you can have?"

    The main reason why we are categorizing tests is, so that we can run different test suites. Eg.

    • Unit tests on Gated Checkin
    • Integration tests after each check in on the build server
    • All tests including the functional tests in the nightly build

    Samples for Naming of test projects
    Test.Unit.WebUI: This test project, tests the WebUI project, and is independent of external resources.
    That means all tests must pass.
    Test.Integration.WebUI: This test project tests the WebUI and depends from other external resources (Eg. probably needs a database, webservices etc.).
    That means if any external resource are unavailable, the tests will fail.
    Tests.Functional.SilverlightUI: Tests the Silverlight UI from an end user perspective by clicking around in the application

    Unit test project
    Figure: Good example - Naming for a Unit Test Project

    Samples Naming of test methods

                                [TestMethod]
                                public void Test_Client()
                                
    Bad example: There is no way to guess what this test does; you have to read the source
                                [TestMethod]
                                public void PubSubServiceConnectTest_AuctionOk_AuctionInfoReturned()
                                
    Good Example: We are testing PubSubService.Connect under the scenario that the "Auction status is OK" with an expected behaviour that data is returned

    Sample Code for Integration Tests:

    using System;
    using System.Collections;
    using System.Data;
    using System.Data.SqlClient;
    using NUnit.Framework;
    using SSW.NetToolKit.BusinessService;
    using SSW.NetToolKit.DataAccess;
    
    namespace SSW.NETToolkit.IntegrationTests
      {
      [TestFixture]
      Public class CustomerTests
        {
        BusinessRules business=new BusinessRules(); 
    
        [Test]
        public void OrderTotal_SimpleExampleInput()
            {
            decimal calculatedGrandTotal = business.CalculateOrderGrandTotal(10248);
            int expected = 440;
            Assert.AreEqual(expected, calculatedGrandTotal, "Calculated grand total didn't match the expect
            }
    
        [Test]
        public void OderTotal_Discounts()
            {
            decimal calculatedGrandTotal = business.CalculateOrderGrandTotal(10260);
            decimal expected = 1504.65m;
            Assert.AreEqual(expected, calculatedGrandTotal, "Calculated grand total didn't match the expecte
            }
    
        [Test]
        public void RoundingTest_RoundUp()
            {
            Assert.AreEqual(149.03, business.ApplyRounding(149.0282m), "Incorrect rounding rules applied for
            }
    
        [Test]
        public void RoundingTest_RoundDown()
            {
            Assert.AreEqual(149.02, business.ApplyRounding(149.0232m), "Incorrect rounding rules applied 
            }
    
        [Test]
        public void RoundingTest_NoRoundingNeeded()
            {
            Assert.AreEqual(149.02, business.ApplyRounding(149.02m), "Incorrect rounding rules applied for 
            }
                                     
        [Test]
        public void RoundingTest_BorderCondition()
            {
            Assert.AreEqual(149.02, business.ApplyRounding(149.025m), "Incorrect rounding rules applied for
            }
                                   
        }
      }

    Test Generation Settings
    Figure: This rule is consistent with the Visual Studio default

    We have a program called SSW .Net Toolkit that implements this.

    Tip: You can create a test project using the Unit Test Wizard: Test > Add New Test

    Add New Test
    Figure: Unit Test Wizard 1
    Create Unit Tests
    Figure: Unit Test Wizard 2
  8. Do you know when you are adding a unit test for an edge case - the naming convention should just be the issue ID?

    When you are adding a unit test for Email validation it is easy to come up with obvious names eg. "TestValidEmails()" and "TestInvalidEmails()".
    But when you have just read a bug report and there are 5 steps to reproduce it, people come up with all sorts of long wacky names to explain the 5 steps.

    TestEnterTitleSaveGoBackResave
    Figure: Bad example - The test name reads like repro steps.
    TestProj11
    Figure: Good example - The test is the issue name and you can understand by visiting the issue in your bug database
     ///
     Test case where a user can cause an application exception on the
     Seminars webpage
     
     1. User enters a title for the seminar
     2. Saves the item
     3. Presses the back button
     4. Chooses to resave the item
     
     See: https://server/jira/browse/PROJ-11
     ///
    
    [Test]
    public void TestProj11()
    {
        
    }
    Figure: Good comments for the unit test
  9. Do you have unit tests outside the project - not inside?Red star

    Unit tests should be written outside the project because:

    • Separation of duties - the guys writing the unit tests should not have knowledge of the internals of the project - they should see it as a black box
    • Most developers don't want the unit tests deployed - even with conditional compilation you leave references behind
    • The projects will be smaller without the unit tests
    outside project
    Figure: Bad Structure;
    Figure: Bad Structure - each layer for UnitTests are related to each other, meaning it's not easy to maintain;
    Figure: Good Structure - each layer for UnitTests are isolated from each other, meaning it's easier to maintain (adding new project and test, removing a project and its test cases);
  10. Do you write Integration Test for Dependencies - e.g. DLLs?Red star

    Dependant code is code that relies on other factors like methods and classes inside a separate DLL. Because of the way the .NET works assemblies are loaded as required by the program (this is what we call the JIT compiler). Thus, when a DLL goes astray, you will only find out at run time when you run a form/function that uses that DLL. These run time errors can occur when you have not packaged DLLs in your release or if the versions are incompatible. Such errors cause the following exceptions:

    • An unhandled exception ("System.IO.FileNotFoundException") occurred in SSW.NETToolkit.exe.
    • System.IO.FileLoadException The located assembly's manifest definition with name 'SSW.SQLDeploy.Check' does not match the assembly reference.

    These errors can be fixed by writing a integration test to check all referenced assemblies in a project.

    Sample Code:

    [Test]
    public void ReferencedAssembliesTest()
    {
        // Get the executing assembly
        Assembly asm = Assembly.GetExecutingAssembly();
        // Get the assemblies that are referenced
        AssemblyName[] refAsms = asm.GetReferencedAssemblies();
        // Loop through and try to load each assembly
        foreach( AssemblyName refAsmName in refAsms)
        {
            try
            {
                Assembly.Load(refAsmName);
    
         	}
            catch(FileNotFoundException)
            {
                // Missing assembly
                Assert.Fail(refAsmName.FullName + " failed to load");
            }
        }
    }
    Figure: This code is a unit test for checking that all referenced assemblies are able to load.

    We have a program called SSW .Net Toolkit that implements this.

  11. Do you *not* write integration tests for Database Dependencies - e.g. Database Schema, Datasets?Red star

    If we did our Data Access layer manually we should be testing this. Instead we recommend using:

    1. LINQ (VS2008) or Code Generators (VS2005)
    2. Use a _regenerate.bat
    3. Add a unit test to create the database and check that "reconcile" works

      We have a program called SSW SQL Deploy that implements this.

    Figure: The dataset and the database schema are not consistent (Bad)
    Figure: The dataset and the database schema are consistent (Good)

    As long as you have a _regenerate.bat file your schema will not get out of sync.

  12. Do you write integration tests for Web Service Dependencies?Red star

    With web services you should:

    1. Use a _regenerate.bat to regenerate the proxy classes (See our Rules To BetterWindows Forms for more information.)
    2. Compare old WSDL with new WSDL to pick up changes and email the diff file to developers
    3. Minimum Tests - Call the web service to check it's alive
    4. Performance Test - If it takes longer than 4 seconds to call a methods - it fails
  13. Do you write integration tests for your database configuration?

    We recommend you have test data for your database configuration and reconcile form rather than testing it by opening the UI and doing the configuration manually.

    Figure 1: Get user's confirmation
    Figure 2: Create test database
    Figure 3: Reconcile

    Sample Code:

    /// <summary> 
    /// Create a temp database
        and do reconcile then drop it 
    ///</summary>
    ///<param name="Config">DBConfig
        Config</param>
    ///<returns>string</returns> 
    public static string ConfigAndReconcileTester(DBConfig Config) 
            {  
                try {
                    _deployForm = new DatabaseDeployForm(Config);
                    _deployForm.IsATest = true;
                    if(_deployForm.ShowDialog() != DialogResult.OK)
                    {
                        return "Failed to create test database";
                    }
                    Config.IsReconcile = true;
                    DialogResult reconciled = new 
                    SqlDeployHelper(Config).BeginProcess();              
    
                    if(reconciled == DialogResult.OK)
                    {
                        return SUCCESS_INFO;
                    }          
                    return "Reconciliation failed";
                }
                finally
                {
                    DropTestDB(Config);
                }
    }
                    
    Imports System
    Imports NUnit.Framework
    Imports SSW.Framework.Data
    Imports SSW.Framework.UnitTests
    Import SSW.Framework.Data.SqlServer.WindowsUI
    Imports SSW.SQLAuditor.WindowsUI
    
    Namespace Functional
    <TestFixture()> _
    Public Class DataBaseConfigureTests
    Private Const STR_SSWSQLAuditorNorthwindTest As String = "SSWSQLAuditorNorthwindTest"
    
    Private config As DBConfig = New DBConfig()
    
    <SetUp()> _
    Public Sub Setup()
    'configurations for database deploy form
        config.ConnectionBuilder.ConnectionString = String.Empty
        config.CreateScriptsPath = System.IO.Path.Combine(Application.StartupPath + "\..\..\", _
      "DatabaseSQLScripts")
        config.UpgradeScriptsPath = config.CreateScriptsPath  
        config.SampleScriptsPath = System.IO.Path.Combine(config.CreateScriptsPath, "Samples")
        config.NewDatabaseName = STR_SSWSQLAuditorNorthwindTest
        config.IsDatabaseNameEnforced = True
        config.ShouldCreateSamples = True
        config.IsNew = True
        config.IsSampleDatabaseNameEnforced = True
        config.SampleDatabaseName = STR_SSWSQLAuditorNorthwindTest  
        config.DatabaseNamePlaceholder = "[DatabaseName]" 
    End Sub
    
    <Test()> _
    Public Sub ConfigureTest()
        Dim testResult As String = String.Empty
    
        'get user's confirmation to continue test  
        If MessageBox.Show("This
            will create the sample database and reports and then delete them." _
        + Environment.NewLine + " Do you want to continue?", "Continue", MessageBoxButtons.OKCancel, _
        MessageBoxIcon.Exclamation) = DialogResult.Cancel Then
    	Return End If
      testResult = DatabaseConfigTester. ConfigAndReconcileTester(config)
        Assert.AreEqual(testResult, DatabaseConfigTester.SUCCESS_INFO,
            testResult)
    End Sub
    
    <TearDown()> _
    Public Sub RestoreToDefault()
    End Sub End Class End Namespace
    
  14. Do you write integration tests for your reporting service configuration?

    We also recommend you have test data for your reporting service configuration. However, before configuring the reporting service, you should specify a data source for the reports using your database deploy form.

    Figure: Reporting service config test

    Sample Code:

    /// 
                            <summary> 
                                /// Properties ApplicationName, ReportDirectoryName,
                                    ReportDirectoryName, ///
                                        ReportRdlFolderPathAlternative, ReportManagerUrl and ReportServerWebServiceUrl
    /// in rsConfig
        should be configed before start testing. /// </summary> ///
                <param name="dbConfig"></param> ///
                    <param name="rsConfig"></param> 
                            public static void
                                    StartTest(DBConfig dbConfig,RSConfig rsConfig) { (new DatabaseDeployForm(dbConfig)).ShowDialog(); 
                                            //get a data source from database deploy form rsConfig.DataSourceConnectionString
                                    = dbConfig.ConnectionBuilder.ConnectionString;
                                        rsConfig.DataSourceName =
                                            dbConfig.ConnectionBuilder.DatabaseName; //pop up a reporting
                                                service config form PublishReportsForm prForm 
    = new PublishReportsForm(new ReportSetupControl(),rsConfig);
                prForm.IsAUnitTest =
                true; Assert.AreEqual(prForm.ShowDialog(),DialogResult.OK,"Cancled by user"); } } 


    usingSystem;usingSystem.Windows.Forms;
    usingSystem.Collections.Generic;
    usingSystem.Text;
    usingNUnit.Framework;
    usingSSW.Framework.Data;
    usingSSW.Framework.UnitTests;
    usingSSW.Framework.Data.SqlServer.WindowsUI;
    usingSSW.Framework.ReportingServices.WindowsUI;
    usingSSW.LinkAuditor.Common;
    namespace
       SSW.LinkAuditor.UnitTests.Functional { [TestFixture]
       publicclassReportingServiceConfigTests { 
       privateconststring STR_SSWLinkAuditor = "SSWLinkAuditor"; private DBConfig _dbConfig = new
       DBConfig(); RSConfig _rsConfig = new
       RSConfig(); [SetUp] public
       void SetUp()
       { //configurations for database deploy form
       _dbConfig.ConnectionBuilder.ConnectionString = String.Empty; _dbConfig.CreateScriptsPath 
       = System.IO.Path.Combine(Application.StartupPath
       + @"\..\Database", "Create Scripts"); _dbConfig.UpgradeScriptsPath
       = _dbConfig.CreateScriptsPath;
       _dbConfig.SampleScriptsPath =
       _dbConfig.CreateScriptsPath; _dbConfig.NewDatabaseName 
       = STR_SSWLinkAuditor; _dbConfig.IsDatabaseNameEnforced
       = false; _dbConfig.ShouldCreateSamples
       = false; _dbConfig.IsNew
       = true; _dbConfig.IsSampleDatabaseNameEnforced
       = false; _dbConfig.SampleDatabaseName
       = STR_SSWLinkAuditor;
       _dbConfig.DatabaseNamePlaceholder = "[DatabaseName]"; //configurations
           for reporting service config form _rsConfig.ApplicationName 
       = "SSW Link Auditor"; _rsConfig.ReportDirectoryName 
       = _rsConfig.ApplicationName 
       + " Reports"; _rsConfig.ReportRdlFolderPath 
       = AppDomain.CurrentDomain.BaseDirectory + 
       @"\Report\RS2000"; _rsConfig.ReportRdlFolderPathAlternative 
       = AppDomain.CurrentDomain.BaseDirectory + 
       @"\Reports\RS2005"; _rsConfig.ReportManagerUrl 
       = "http://localhost/Reports"; _rsConfig.ReportServerWebServiceUrl 
       = "http://localhost/reportserver/ReportService.asmx"; }
           [Test] public void ConfigTest()
        {  ReportingServiceConfigTester.StartTest(_dbConfig,_rsConfig);
    
         } } }
    
    
    
  15. Do you write integration tests to validate your web links?

    If you store your URL references in the application settings, you can create integration tests to validate them.

    Figure: URL for link stored in application settings

    Sample Code: How to test the URL

    	[Test]
            public void urlRulesToBetterInterfaces()
            {
                HttpStatusCode result = WebAccessTester.GetWebPageStatusCode(Settings.Default.urlRulesToBetterInterfaces);
                Assert.IsTrue(result == HttpStatusCode.OK, result.ToString());
            }
    	
    Sample Code: Method used to verify the Page
    	 public class WebAccessTester
        {        
            public static HttpStatusCode GetWebPageStatusCode(string url)
            {
                HttpWebRequest req = ((HttpWebRequest)(WebRequest.Create(url)));
                req.Proxy = new WebProxy();
                req.Proxy.Credentials = CredentialCache.DefaultCredentials;
                HttpWebResponse resp = null;
                try
                {
                    resp = ((HttpWebResponse)(req.GetResponse()));
                    if (resp.StatusCode == HttpStatusCode.OK)
                    {
                        if (url.ToLower().IndexOf("redirect") == -1 && url.ToLower().IndexOf(resp.ResponseUri.AbsolutePath.ToLower()) == -1)
                        {
                            return HttpStatusCode.NotFound;
                        }
                    }
                }
                catch (System.Exception ex)
                {JavaScript
                    while (!(ex == null))
                    {
                        Console.WriteLine(ex.ToString());
                        Console.WriteLine("INNER EXCEPTION");
                        ex = ex.InnerException;
                    }
                }
                finally
                {
                    if (!(resp == null))
                    {
                        resp.Close();
                    }
                }
                return resp.StatusCode;
            }
        }
        
  16. Do you test your JavaScript?

    The need to build rich web user interfaces is resulting in more and more JavaScript in our applications.

    Because JavaScript does not have the safeguards of strong typing and compile time checking, it is just as important to unit test your JavaScript as your server side code.

    You can write unit tests for JavaScript using:

    1. JsUnit
      Code Metrics
      Figure: Bad Example ā€“ Not updated for several years
    2. QUnit

      QUnit focuses on testing JavaScript in the browser, while providing as much convenience to the developer as possible. Blurb from the site:

      QUnit is a powerful, easy-to-use JavaScript unit test suite. It's used by the jQuery, jQuery UI and jQuery Mobile projects and is capable of testing any generic JavaScript code

      QUnit was originally developed by John Resig as part of jQuery. In 2008 it got its own home, name and API documentation, allowing others to use it for their unit testing as well.

      You should have a section on your zsValidate page that runs your JavaScript unit tests.

      QUnit test and the test results
      Figure: Good Example ā€“ An example QUnit test and the test results
  17. Do you test Fragile Code - e.g. Regular Expressions?Red star

    Fragile code is code that often breaks and are difficult to understand. An example of this is with Regular Expressions.

    When using regular expressions, because of their rather cryptic syntax, They should always be associated with at least two sample tests - one that passes the regular expression and one that fails. These regular expressions should be stored in a central location, i.e. a database or a class file.

    A Unit Test should be written such that it loops through each of these regular expressions and tests it against the supplied test cases.

    Sample Code:

    public bool IsValidEmail(string strIn)
            {
               invalid = false;
                if (String.IsNullOrEmpty(strIn))
                    return false;
                // Use IdnMapping class to convert Unicode domain names. 
                try
                {
                    strIn = Regex.Replace(strIn, @"(@)(.+)$", this.DomainMapper,
                                          RegexOptions.None, TimeSpan.FromMilliseconds(200));
                }
                catch (RegexMatchTimeoutException)
                {
                    return false;
                }
                if (invalid)
                    return false;
                // Return true if strIn is in valid e-mail format. 
                try
                {
                    return Regex.IsMatch(strIn,
                          @"^(?("")(""[^""]+?""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
                         @"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9]{2,24}))$",
                          RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));
                }
                catch (RegexMatchTimeoutException)
                {
                    return false;
                }
            }
    
    Figure: A function to validate email addresses
    [TestMethod]
            public void RegexUtilitiesIsValidEmail_IsNotEmailValid_EmailIsNotValid()
            {
                string email = "in@correct_email@ssw.com";
                bool expected = false;
    
                RegexUtilities address = new RegexUtilities();
    
                bool response = address.IsValidEmail(email);
    
                Assert.AreEqual(expected, response, "Invalid Email");
    
            }
    
    Figure: Tests a bad case
    [TestMethod]
            public void RegexUtilitiesIsValidEmail_IsEmailValid_EmailIsValid()
            {
                string email = "correct_email@ssw.com";
                bool expected = true;
    
                RegexUtilities address = new RegexUtilities();
    
                bool response = address.IsValidEmail(email);
    
                Assert.AreEqual(expected, response, "Valid Email");
              
            }
    
    Figure: Tests a good case
  18. Do you have tests for difficult to spot errors - e.g. Arithmetic, Rounding, Calculations?Red star

    By difficult to spot errors we mean errors that do not give the user a prompt that an error has occurred. Things such as: Arithmetic, Rounding or Calculations should have unit tests written for them.

    Sample Code:

    DataAccess |Utilities.cs
    
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace DataAccess {
          public class Utilities {
                              // A business rule – it should be unit tested
                public decimal CalculateTotal(List<myitem> items) {
                      decimal total = 0.0M;
                      foreach (MyItem i in items) {
                            total += i.UnitPrice * (1 - i.Discount);
                      }
                      return total;
                }     
          }
    }
    		
    Figure: Function to calculate a total for a list of items

    For a function like this, it might be simple to spot errors when there are one or two items, but if you were to calculate the total for 50 items, then the task of spotting an error isn't so easy. That's why a unit test should be written so that you know when the function doesn't work.

    Sample Test:

    DataAccess.Tests | UtilitiesTest.cs
    
    using System;
    using System.Collections.Generic;
    using System.Text;
    using NUnit.Framework;
    namespace DataAccess.Tests {
    
          [TestFixture()]
          public class UtilitiesTests {
    
                [Test()]
                public void CalculateTotalSimpleTest() {
                      List<myitem> items = new List<MyItem>();
                      items.Add(new MyItem(12.50M));
                      items.Add(new MyItem(4.75M));
                      items.Add(new MyItem(9.05M));
                      items.Add(new MyItem(6.55M));
                      items.Add(new MyItem(20.12M));
     
                      decimal expected = 52.97M;
                      Utilities u = new Utilities();
                      decimal actual = u.CalculateTotal(items);
    
                      Assert.AreEqual(expected, actual);
                }
    
                [Test()]
                public void CalculateTotalWithDiscountTest() {
                      List<myitem> items = new List<myitem>();
                      items.Add(new MyItem(12.50M, 0.1M));
                      items.Add(new MyItem(4.75M));
                      items.Add(new MyItem(9.05M));
                      items.Add(new MyItem(6.55M));
                      items.Add(new MyItem(20.12M));
     
                      decimal expected = 51.72M;
                      Utilities u = new Utilities();
                      Assert.IsNotNull(u);
    
                      decimal actual = u.CalculateTotal(items);
                      Assert.AreEqual(expected, actual);
                }
          }
    }
    
    DataAccess | MyItem.cs
    
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace DataAccess {
          public class MyItem {
                public MyItem(decimal unitPrice) {
                      _unitPrice = unitPrice;
                }
    
                public MyItem(decimal unitPrice, decimal discount) : this(unitPrice) {
                      _discount = discount;
                }
    
                private decimal _unitPrice;
                private decimal _discount;
    
                public decimal Discount {
                      get { return _discount; }
                      set { _discount = value; }
                }
    
                public decimal UnitPrice {
                      get { return _unitPrice; }
                      set { _unitPrice = value; }
                }
          }
    }
    		
    Figure: Test calculate total by checking something we know the result of. (Note: it doesn't need a failure case because it isn't a Regex.)

    We have a program called SSW .Net Toolkit that implements this.

  19. Do you have tests for Performance? Red star

    Typically there are User Acceptance Tests that need to be written to measure the performance of your application. As a general rule of thumb, Forms should not load in more than 4 seconds. This can be done using NUnit.

    Sample Code:

    
        public abstract class FormTestBase<F>
            where F : Form, new()
        {
            protected TimeSpan _ExpectedLoadTime = TimeSpan.FromSeconds(4);
    
            [Test]
            public void LoadTest()
            {
                Stopwatch stopwatch = new Stopwatch();
    
                stopwatch.Start();
    
                F testForm = new F();
                testForm.Show();
                testForm.Close();
    
                stopwatch.Stop();
    
                Console.WriteLine("Form [{0}] took {1:#,##0.0} seconds to open", typeof(F), stopwatch.Elapsed.TotalSeconds);
                Assert.IsTrue(stopwatch.Elapsed < _ExpectedLoadTime, 
                    string.Format("Loading time ({0:#,##0.0} seconds) exceed the expected time ({1:#,##0.0} seconds).", 
                    stopwatch.Elapsed.TotalSeconds, _ExpectedLoadTime.TotalSeconds));
            }
        }
        
        
        [TestFixture]
        public class LoginFormTests : FormTestBase<LoginForm>
        {
        }
        
        [TestFixture]
        public class MainFormTests : FormTestBase<MainForm>
        {
        }
    		
    Figure: This code tests that the LoginForm and MainForm load in under 4 seconds

    We have a program called SSW .Net Toolkit that implements this.

  20. Do you run Unit Tests in Visual Studio?Red star

    When you build the test project, the tests appear in Test Explorer. If Test Explorer is not visible, choose Test on the Visual Studio menu, choose Windows, and then choose Test Explorer.

    Code Metrics
    Figure: Test Explorer

    As you run, write, and rerun your tests, Test Explorer displays the results in default groups of Failed Tests, Passed Tests, Skipped Tests and Not Run Tests. You can change the way Test Explorer groups your tests.
    You can perform much of the work of finding, organizing and running tests from the Test Explorer toolbar.

    Code Metrics
    Figure: Run Tests

    You can run all the tests in the solution, all the tests in a group, or a set of tests that you select. Do one of the following:

    • To run all the tests in a solution, choose Run All
    • To run all the tests in a default group, choose Run... and then choose the group on the menu
    • Select the individual tests that you want to run, open the context menu for a selected test and then choose Run Selected Tests.

    The pass/fail bar at the top of the Test Explorer window is animated as the tests run. At the conclusion of the test run, the pass/fail bar turns green if all tests passed or turns red if any test failed.

  21. Do you have a integration test for your send mail code?

    The code below could help you test your send mail code:

    DotNetOpenMailProvider provider = new DotNetOpenMailProvider();
    NameValueCollection configValue = new NameValueCollection();
    configValue["smtpServer"] = "127.0.0.1";
    configValue["port"] = "8081";
    provider.Initialize("providerTest", configValue);
    
    TestSmtpServer receivingServer = new TestSmtpServer();
    try
    {
        receivingServer.Start("127.0.0.1", 8081);
        
        provider.Send("phil@example.com", 
        
                    "nobody@example.com", 
                    "Subject to nothing", 
                    "Mr. Watson. Come here. I need you.");
    }
    finally
    {
        receivingServer.Stop();
    }
    
    // So Did It Work?
    Assert.AreEqual(1, receivingServer.Inbox.Count);
    ReceivedEmailMessage received = receivingServer.Inbox[0];
    
    Assert.AreEqual("phil@example.com", received.ToAddress.Email);
                            
    Figure: This code could help you validate the send mail code
  22. Do you have a /HealthCheck (was /zsValidate) page to test your website dependencies?

    There are two kinds of errors, coding errors and deployment errors. Coding errors should be found during development by compiling or debugging, while deployment errors should be find by a validation test.
    Refer to the following rules for details:
    See SSW Rules - Do you have a HealthCheck page to make sure your website is healthy?
    See SSW Rules - Do you have a Validation Page for your web server?

  23. Do you know when functions are too complicated?

    In general you should always be looking to simplify your code (e.g. heavily nested case statements). As a minimum look for the most complicated method you have and check that it needs simplifying.

    In VS 2008, there is inbuilt support for Cyclomatic Complexity analysis.

    1. Go to Developer > Code Metrics > Generate for Solution
      Code Metrics
      Figure: Cyclomatic Complexity analysis tool
    2. Look at the largest Cyclomatic Complexity number and refactor.
      Results from Cyclomatic analysis
      Figure: Results from Cyclomatic analysis these metrics give an indication on how complicated functions are
  24. Do you isolate your logic and remove dependencies on instances of objects?

    If there are complex logic evaluations in your code, we recommend you isoloate them and write unit tests for them.

    Take this for example:

    while ((ActiveThreads > 0 || AssociationsQueued > 0) && (IsRegistered || report.TotalTargets <= 1000 )
        && (maxNumPagesToScan == -1 || report.TotalTargets < maxNumPagesToScan) && (!CancelScan))     
    		
    Figure: This complex logic evaluation can't be unit tested.

    Writing a unit test for this piece of logic is virtually impossible - the only time it is executed is during a scan, and there are lots of other things happening at the same time meaning the unit test will often fail and you won't be able to identify the cause anyway.

    We can update this code to make it testable though.

    • Update the line to this:
      while (!HasFinishedInitializing (ActiveThreads, AssociationsQueued, IsRegistered, 
          report.TotalTargets, maxNumPagesToScan, CancelScan))
      		
      Figure: Isolate the complex logic evaluation.

      We are using all the same parameters - however now we are moving the actual logic to a separate method.

    • Now create the method:
      private bool HasFinishedInitializing(int ActiveThreads, int AssociationsQueued, bool IsRegistered, 
          int TotalAssociations, int MaxNumPagesToScan, bool CancelScan)
      {
         return (ActiveThreads > 0 || AssociationsQueued > 0) && (IsRegistered || TotalAssociations <= 1000 )
             && (maxNumPagesToScan == -1 || TotalAssociations < maxNumPagesToScan) && (!CancelScan);		
      }
      		
      Figure: Function of the complex logic evaluation.

    The critical thing is that everything the method needs to know is passed in, it mustn't go out and get any information for itself and mustn't rely on any other objects being instantiated. A good way to enforce this is to make each of your logic methods static. It has to be completely self contained.

    The other thing we can do now is actually go and simplify / expand out the logic so that it's a bit easier to digest.

    private bool HasFinishedInitializing(int ActiveThreads, int AssociationsQueued, bool IsRegistered, 
        int TotalAssociations, int MaxNumPagesToScan, bool CancelScan)
    {
       //Cancel
       if (CancelScan)     
       { return true; }
    
       //only up to 1000 links if it is not a registered version
       if (!IsRegistered && TotalAssociations > 1000) 
       { return true; }
    
       //only scan up to the specified number of links
       if (MaxNumPagesToScan != -1 && TotalAssociations > MaxNumPagesToScan) 
       { return true; }
    
       //not ActiveThread and the Queue is full
       if(ActiveThreads <= 0 && AssociationsQueued <= 0) 
       { return true; }
    
       return false;
    }		
    Figure: Simplify the complex logic evaluation.

    The big advantage now is that we can unit test this code easily in a whole range of different scenarios!

    [Test]
    public void HasFinishedInitializingLogicTest()
    {
       Validator validator = new Validator();
    
        //Set scenario A
       int activeThreads = 2;
       int associationsQueued = 20;
       bool isRegistered = false;
       int totalAssociations = 1200;
       int maxNumPagesToScan = -1;
       bool cancelScan = false;
    
       bool actual = (bool)Reflection.InvokeMethod("HasFinishedInitializing", validator,
           new object[] {activeThreads, associationsQueued, isRegistered,
           totalAssociations, maxNumPagesToScan, cancelScan});
       Assert.IsTrue(actual, "HasFinishedInitializing LogicTest A failed.");
    
       //Set scenario B
       activeThreads = 2;
       associationsQueued = 20;
       isRegistered = true;
       totalAssociations = 1200;
       maxNumPagesToScan = -1;
       cancelScan = false;
               
       actual = (bool)Reflection.InvokeMethod("HasFinishedInitializing", validator,
           new object[] {activeThreads, associationsQueued, isRegistered,
           totalAssociations, maxNumPagesToScan, cancelScan});
       Assert.IsFalse(actual, "HasFinishedInitializing LogicTest B failed.");
       }	
    		
    Figure: Write unit test for complex logic evaluation.
  25. Do you isolate your logic from your IO to increase the testability?

    If your method is consist of logic and IO, we recommend you isoloate them to increase the testability of the logic.

    Take this for example (and see how we refactor it):

    public static List<string> GetFilesInProject(string projectFile)
    {
        List<string> files = new List<string>();
    
        TextReader tr = File.OpenText(projectFile);
    
        Regex regex = RegexPool.DefaultInstance[RegularExpression.GetFilesInProject];
        MatchCollection matches = regex.Matches(tr.ReadToEnd());
    
        tr.Close();
    
        string folder = Path.GetDirectoryName(projectFile);
    
        foreach (Match match in matches)
        {
            string filePath = Path.Combine(folder, match.Groups["FileName"].Value);
    
            if (File.Exists(filePath))
            {
                files.Add(filePath);
            }
        }
    
        return files;
    }
    Bad - The logic and the IO are coded in a same method.

    While this is a small concise and fairly robust piece of code, it still isn't that easy to unit test. Writing a unit test for this would require us to create temporary files on the hard drive, and probably end up requiring more code than the method itself.

    If we start by refactoring it with an overload, we can remove the IO dependency and extract the logic further making it easier to test:

    public static List<string> GetFilesInProject(string projectFile)
    {
        string projectFileContents;
        using (TextReader reader = File.OpenText(projectFile))
        {
            projectFileContents = reader.ReadToEnd();
            reader.Close();
        }
    
        string baseFolder = Path.GetDirectoryName(projectFile);
    
        return GetFilesInProjectByContents(projectFileContents, baseFolder, true);
    }
    
    public static List<string> GetFilesInProjectByContents(string projectFileContents, string baseFolder, bool checkFileExists)
    {
        List<string> files = new List<string>();
    
        Regex regex = RegexPool.DefaultInstance[RegularExpression.GetFilesInProject];
        MatchCollection matches = regex.Matches(projectFileContents);
    
        foreach (Match match in matches)
        {
            string filePath = Path.Combine(baseFolder, match.Groups["FileName"].Value);
    
            if (File.Exists(filePath) || !checkFileExists)
            {
                files.Add(filePath);
            }
        }
    
        return files;
    }
    Good - The logic is now isolated from the IO.

    The first method (GetFilesInProject) is simple enough that it can remain untested. We do however want to test the second method (GetFilesInProjectByContents). Testing the second method is now too easy:

    [Test]
    public void TestVS2003CSProj()
    {
        string projectFileContents = VSProjects.VS2003CSProj;
        string baseFolder = @"C:\NoSuchFolder";
    
        List<string> result = CommHelper.GetFilesInProjectByContents(projectFileContents, baseFolder, false);
        Assert.AreEqual(15, result.Count);
        Assert.AreEqual(true, result.Contains(Path.Combine(baseFolder, "BaseForm.cs")));
        Assert.AreEqual(true, result.Contains(Path.Combine(baseFolder, "AssemblyInfo.cs")));
    }
    
    [Test]
    public void TestVS2005CSProj()
    {
        string projectFileContents = VSProjects.VS2005CSProj;
        string baseFolder = @"C:\NoSuchFolder";
    
        List<string> result = CommHelper.GetFilesInProjectByContents(projectFileContents, baseFolder, false);
        Assert.AreEqual(6, result.Count);
        Assert.AreEqual(true, result.Contains(Path.Combine(baseFolder, "OptionsUI.cs")));
        Assert.AreEqual(true, result.Contains(Path.Combine(baseFolder, "VSAddInMain.cs")));
    }
    Good - Different test cases and assertions are created to test the logic.
  26. Do you make sure you integration tests include UI (aka form) testing?

    This is only for Web Based apps.... and it is free.

    You should check:

    • Form entry works
    • Validation works

    Use Selenium for this task. Save the recorded C# into your integration project.

  27. Do you know the best unit test framework to use for your project?

    For Windows Applications:

    Unit tests shouldn't only be for developers, it should be able to run on customer's machines as well. Based on this consideration, we still NUnit in order to allow our users to run available tests on their machine. This will help us collect more useful information on client machines and give our products more confidence and professional look.

    As NUnit provides a great UI for this purpose, we recommend you to use NUnit even you've already been working on VSTS.

    Figure: NUnit UI

    For Web Applications:

    With web applications we use MSTest.
    Also see this rule: Do you have a Validation Page for your web server?

  28. Do you know how to run nUnit tests from within Visual Studio?

    Option 1: External tool (not recommended)
    Using NUnit with Visual Studio: To make it easy to use, you need to add it as an External tool in Visual Studio.

    In Visual Studio:

    1. Go to Tools > External Tools
    2. Click “Add” button
    3. Type in:
    • Title: NUnit GUI
    • Command: Location of nUnit.exe file
    • Argument: /run (so that the tests run automatically when started)
    • Initial Directory: $(Target directory)
    Nunit in VS
    Figure: Bad Example - NUnit In Visual Studio

    Option 2: Test Driven .net
    TestDriven.net has better NUnit integration – from both code and Solution Explorer windows.

    use TestDriven.net
    Figure: Better way - Use TestDriven.Net - it has a 'Run Test(s)' command for a single test (above) or...
    GUI bring up action
    Figure: ...you can right click on a project and select 'Test With > NUnit' to bring up the GUI. It is certainly more convenient

    To run unit testing: Tools > NUnit GUI to launch NUnit and run the tests.

    Option 3: Other Tools
    Other Visual Studio tools including Resharper and Coderush have their own integration with NUnit. If you’re already using one of these, installing TestDriven.net is unnecessary.

  29. Do you know the right version and config for Nunit?

    There are multiple version of NUnit and .NET Framework, the following will explain how to use them correctly.

    • if your application was built with .NET Framework 1.1, NUnit 2.2.0 which was built with .NET Framework 1.1 is the best choice if you compact it into the installation package, You then don't need any additional config - it will auto use .NET Framework 1.1 to reflect your assembly;
      • If there is only .NET Framework 2.0 on the client side, how to make it works?
        Just add the yellow into nunit-gui.exe.config (it is under the same folder as nunit-gui.exe), which will tell NUnit to reflect your assembly with .NET Framework 2.0;

        ...
        <startup>
        <supportedRuntime version="v2.0.50727" />
        <supportedRuntime version="v1.1.4322" />
        <supportedRuntime version="v1.0.3705" />
        <requiredRuntime version="v1.0.3705" />
        </startup>
        ...

    • if your application was built with .NET Framework 2.0, then you may get choices:
      • NUnit 2.2.7 or higher (built with .NET framework 2.0) (recommended)
        Then you don't need any extra configuration for NUnit, just follow the default;
      • NUnit 2.2.0 or lower (built with .NET Framework 1.1)
        Then you need to add the yellow statement (see above in this section);
  30. Did you know Visual Studio Team System does the same thing (but we don't recommend it)?

    Visual Studio Team System has unit testing integrated into the IDE. There is a new Test View window and a Test Results window. You can run the unit tests from the right click menu in Visual Studio Team System.

    Figure: New Test View window (run tests from right click)
    Figure: New Test Results window

    You can write unit tests much the same way as in NUnit with a few changes in syntax:

    Test Attribute NUnit Visual Studio Team System
    Test Fixture [TestFixture] [TestClass]
    Set Up [SetUp] [TestInitialize]
    Tear Down [TearDown] [CleanUp]
    Test [Test] [TestMethod]

    VSTS is a great integrated toolset and there are many features in it you need. However because there is no existing GUI to run these tests outside of VSTS (like you can with nUnit) there is no way of having this menu option in your application.
    Therefore we *don't recommend* it if you also want to Run Unit Tests from the Help Menu

  31. Do you have a standard 'Help' menu that includes a way to run your unit tests?

    Thanks Adam for coming to our user group in Boston and presenting your session "Rules to Healthier Code"
    While I had used most of your best tips I'd never thought to put them in concert as you have. In concert I mean together, I have used and demo'd NUNIT, FxCop. MBUnit and almost the full range of third party tools you suggest. I tend to use them when I need to fix a particular problem.
    BUT I never thought to use all of them together. Like your tip to include NUnit with the finished product! Just brilliant! It still makes me laugh at the simplicity of it. I get the flash of a cool validation tool and user validation for the work I've already done in NUnit. Its good marketing, good salesmanship and good engineering! Its just elegant, I might include it with my setup as part of the install! "Click here to validate your install" It'll force me to be more diligent about NUnit, but now there is a better reason for the diligence.
    Again thanks for the great presentation Wednesday. I'll be re-working the concept into my own UG programs here in New Hampshire.

    Pat Tomey
    www.4square.net

    Your standard help menu should include an option to run your Unit Tests. Everybody knows the importance of Unit tests for the middle tier. However, Unit Tests are also important to capture problems that occur on other peoples' machines so that users can perform a quick check when a product is not behaving correctly. This is important for troubleshooting during support calls and enables your customers to do a Health Check on the product.

    And yes, there are many tests that can be written that will pass on the developers PC - but not on the users PC. e.g. Ability to write to a directory, missing dlls, missing tables in the schema etc.

    Note: Adding this option requires you to include NUnit in your setup.exe (See Include all the files needed in our Wise Standard)

    Figure: Standard Help menu should give you an option to Run Unit Tests to check the users' environment (Good)
    Figure: Obviously the red indicates that there is a problem with a Unit Test (Good)

    We Create:

    1. VS.NET for Web
    2. NUnit for Windows
    TFS needs to include a unit test runner

    We have a rule Do you know the Seven items every Help menu needs?

    We have a program called SSW .Net Toolkit that implements this.

  32. Do you make sure that the test can be failed?

    You should make sure that the unit test you created can actually fail. It is very easy and common to create a test which never fails. It is very important to take baby steps and not to make any assumptions.

    One of the ways you can do this is by returning NotImplementedException() from the method you are writing test for. For Example:

        [Test]
        public void Test_ABC_MethodReturns_XYZ()
        {
            MyClass classObj = new MyClass();
            Assert.IsNotNull(classObj.Get_XYZ());
        }
        
        
        //The method to test in class MyClass ...
        public int Get_XYZ()
        {
            throw new NotImplementedException("Write a test");
        }
                        
    Figure: Example of how to use NotImplementedException
  33. Do you have a Continuous Build Server?

    A continuous build server monitors the Source Control repository and, when something changes, it will update, build and test the software. If something is wrong, notifications are sent out via email.

  34. Do you unit test your database?

    We've all heard of writing unit tests for code and business logic, but what happens when that logic is inside SQL server?

      With Visual Studio, you can write database unit tests. These are useful for testing out:
    1. Stored Procedures
    2. Triggers
    3. User defined functions
    4. Views

    These tests can also be added to the same library as your unit, web and load tests.

    Add a new test
    Figure 1 - Database Unit Test
    Write the unit test
    Figure 2 - Writing the unit test against a stored proc
  35. When you record VS 2010 coded UI tests, make them less brittle

    Coded UI test
    Figure: You record a coded UI test by clicking the buttons "2", "+" and "2"
     public void Calculator_Add2Plus2_Equals4()
            {
                    #region Variable Declarations
                   WinButton uIItem2Button = this.UICalculatorWindow.UIItemWindow.UIItem2Button;
                   WinButton uIAddButton = this.UICalculatorWindow.UIItemWindow1.UIAddButton;
                   WinButton uIEqualsButton = this.UICalculatorWindow.UIItemWindow2.UIEqualsButton;
                   #endregion
    
                   // Launch '%SystemRoot%\System32\calc.exe'
                   ApplicationUnderTest uICalculatorWindow = ApplicationUnderTest.Launch(this.Calculator_Add2Plus2_Equals4Params.UICalculatorWindowExePath, this.Calculator_Add2Plus2_Equals4Params.UICalculatorWindowAlternateExePath);
    
                   // Click '2' button
                   Mouse.Click(uIItem2Button, new Point(12, 8));
    
                   // Click 'Add' button
                   Mouse.Click(uIAddButton, new Point(10, 19));
    
                   // Click '2' button
                   Mouse.Click(uIItem2Button, new Point(9, 11));
    
                   // Click 'Equals' button
                   Mouse.Click(uIEqualsButton, new Point(24, 33));
            }
                                
    Figure: You get this beautiful coded UI test

    These tests could fail in the future, if the buttons were resized to be smaller. So to make them less brittle change them to 1, 1 .

Acknowledgments

Adam Cogan
Eric Phan
Mark Liu
Troy Magennis


Benefit from our knowledge and experience!

SSW is the industry leader in Custom Software Development, Software Auditing & Developer Training.

Call us on +61 2 9953 3000 or email us for a free consultation

What does it cost? Iā€™m not in Australia. Can you still help?