Rules to Better Unit Tests

​​

Hold on a second! How would you like to view this content?
Just the title! A brief blurb! Gimme everything!
  1. Do you know why tests are important?

    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 minimize 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?

    Here are some of the common techniques used for testing software.

      ​​​​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 behaviour in isolation
    • 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 and nSubstitute

      Integrations Tests​

    • They are coded by a developer
    • Slower
    • Test the interaction of components eg. Databases, Web Services

    Subcutaneous Tests

    Subcutaneous (as in just benath the skin) are a type of integration/functional test that operate just below the UI -  and are good for automated functional testing that would otherwise be difficult to achive by manipulating the UI itself. 

    • Written by developers
    • Test the full underlying behaviour of your app but bypasses the UI
    • Requires business logic to be implemented in the API / middle layer and not in the UI.
    • Tests can be much easier to write than using technologies that drive a UI (such as Selemium)

      Load Tests​​

    • Setup by developers
    • Simulate expected load on your application
    • Use the performance stats as a baseline for regression. You don't want to decrease performance in your application.
    • Tip: try to execute these from the cloud

      Stress Tests

    • Setup by developers
    • Hit your application very hard, and try to see where your limits are (CPU, Network, Memory)​

    The Testing Pyramid

    TestPyramid.png
    Figure: the testing pyramid

    The concept of a testing pyramid was introduced by ​Mike Cohn.
    It's a metaphor that gives a guideline on how many tests we should write in each area.

    At the bottom of the pyramid are small, isolated unit tests. These should be simple, easy to write and fast to execute. Our projects should aim to have many of these tests. As you move up the pyramid, complexity (such as the number of involved services) increases. So these tests become progressively harder to wite and slower to run. You should aim to write fewer tests as you move up the pyramid.



  3. Do you know what unit tests to write and how many?

    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
    • When errors can be difficult to spot - e.g. Rounding, arithmetic, calculations

    Unit tests should not be written for:

    • Dependencies - e.g. DLLs Run time errors (JIT)
    • Dependencies - e.g. Database Schema, Datasets, Web Services
    • Performance - e.g. Slow forms, Time-critical applications
    • 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.
  4. Do you make sure that the test can be failed?

    ​You should make sure that the unit tests you create can actually fail. A test that never fails is not useful to anyone.​

    This is a fundamental principle in Test Driven Development (TDD) called Red/Green/Refactor.

    A common approach is by returning NotImplementedException() from the method you are writing tests for. For Example:

    [Test]
    public void ShouldAddTwoNumbers()
    {
       var calculator = new Calculator();
       var result = calculator.Sum(10, 11);


       Assert.Equal(21, result);
    }

    // The method to test in class Calculator ...
    public int Sum(int x, int y)
    {
       throw new NotImplementedException();
    }

    Bad Example: The test fails by throwing a NotImplementedException​​​​

    This test fails for the wrong reason, by throwing a NotImplementedException. In production, this is not a valid reason for this test to fail. 

    A better approach would be to return a value that is invalid:

    // The method to test in class Calculator ...
    public int Sum(int x, int y)
    {
       return 0;
    }

    Good Example: The test fails by returning an invalid result​​​​

    In this case, the test will fail because the behavior is incorrect. It is not correctly adding the two numbers.

  5. Do you write unit tests to confirm bugfixes? (aka Red-Green-Refactor)

    ​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 the test fail, then fix the bug and watch the test pass. This is also known as Red-Green-Refactor.

    Tip: you can then reply to the bug report with "Done + Added a unit test so it can't happen again"

  6. Do you know the most popular unit testing frameworks for .NET Core applications?

    There are three main frameworks to add unit testing. The good news is that they are all acceptable choices:
    • They all have test runner packages for running tests directly from Visual Studio
    • They all have console-based runners that can runs tests as part of a CI/CD pipeline
    • They differ slightly in syntax and feature set

    ​NUnit​​

    The NUnit project deserves recognition for being the first powerful and open source unit test framework for the .NET universe – and it’s still a solid choice today. Because NUnit has an open-source .NET UI control for running tests, NUnit is still SSW’s preferred choice for embedding unit tests and a runner UI inside a windows application.

    xUnit.net – (Recommended)​​

    xUnit.net is a newer framework – written by the original creator of NUnit v2 to create a more opinionated and restrictive framework to encourage TDD best practice. For example, when running xUnit tests, the class containing the test methods is instantiated separately for each test so that tests cannot share data and​​​ can run in parallel.

    xUnit.net is currently the most popular framework - and is ​​even used by the .NET Core team. 

    xUnit.net is the default choice for .NET Core web applications and APIs at SSW.

    MSTest​

    MSTest ​is Microsoft's testing framework. In the past this was a poor choice as although this was the easiest framework to run from Visual Studio, it was extremely difficult to automate these tests from CI/CD build servers. These problems have been completely solved with .NET Core but for most C# developers this is “too little, too late” and the other unit testing frameworks are now more popular.

  7. Do you know the important documents to get started on Unit Testing?

    ​Check out the basic readings to get an understanding of quality development:
  8. Do you have a Continuous Integration (CI) Server?

    ​​​A continuous integration server monitors the Source Control repository and, when something changes, it will checkout, build and test the software. If something is wrong, notifications are sent out e.g. via email or Teams.​

    TIP: Azure DevOps and GitHub provide online build agents with a free tier to get you started 
  9. Do you follow naming conventions for tests and test projects?

    Test Projects​​

    Tests typically live in separate projects – and you usually create a project from a template for your chosen test framework.
    Because your test projects are startup projects (in that they can be independently started), they should target specific .NET runtimes and not just .NET Standard.
    A unit test project usually targets a single code project.

    Project Naming​

    Integration and unit tests should be kept separate and should be named to clearly distinguish the two.
    This is to make it easier to run only unit tests on your build server (and this should be possible as unit tests should have no external dependencies) 
    Integration tests require dependencies and often won't run as part of your build process.  These should be automated later in the DevOps pipeline.

    ​Test Project Location​

    Test projects can be located either:

    • Directly next to the project under test – which makes them easy to find, or
    • In a separate tests location – which makes it easier to deploy the application without tests included.
    clean-architecture-naming.png
    Figure: In the above project, the tests are clearly placed in a separate location which makes it easy to deploy to production without them. It’s easy to tell which project is under test and what style of tests will be found in each test project. https://github.com/jasontaylordev/CleanArchitecture​

    ​Naming Conventions for Tests​​

    There are a few “schools of thought” when it comes to naming the tests themselves. 
    Internal consistency within a project is important.
    It’s usually a bad idea to name tests after the class or method under test – as this naming can quickly get out-of-sync if you use refactoring tools – and one of the key benefits from unit testing is the confidence to refactor!
     
    Remember that descriptive names are useful – but the choice of name is not the developer’s only opportunity to create readable tests.
    • Write tests that are easy to read by following the three A's (Arrange, Act, and Assert)
    • Use a good assertion library to make test failures informative (e.g. Shouldly or FluentAssertions)
    • Use comments and refer to bug reports to document the “why” when you have a test for a specific edge-case. 
    • Remember that the F12 shortcut will navigate from the body of your test straight to the method you’re calling.
    • The point of a naming convention is to make code more readable, not less - so use your judgement and call in others to verify your readability.
    bad-naming.png
    Figure: Bad Example - From the test explorer view you cannot tell what a test is meant to test from the name

    [Method/PropertyName]_Should_[ExpectedBehavior]_When_[StateUnderTest]

    Figure: The "should" naming convention is effective – it encourages developers to clearly define the expected result upfront without requiring too much verbosity.
     
    The following test names use the "should" naming convention:

    Withdraw_Should_ThrowException_When_InvalidAccountInfo

    Checkout_Should_Add10Precent_When_CountryIsAustralia

    Purchase_Should_Suceed_When_BalanceIsWithinCreditLimit

    Figure: Good Examples - Without looking at code, I know what the unit tests are trying to do

    A list of other suggested conventions can be found here: 7 Popular Unit Test Naming Conventions.

  10. Do you know how to 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 expectations 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​​
  11. Do you have tests for difficult to spot errors - e.g. Arithmetic, Rounding, Regular Expressions?

    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 Regular Expressions should have unit tests written for them.

    Sample Code:
    unit test - arithmetic code.jpg

    ​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:

    unit test - arithmetic tests.jpg

    ​Figure: Test calculates total by checking something we know the result of. (Note: it doesn't need a failure case because it isn't a Regex.)​
  12. 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 Visual Studio, there is inbuilt support for Cyclomatic Complexity analysis.​​​

    ​1. Go to Developer > Code Metrics > Generate for Solution

    CodeMetrics.gif
    Figure: Cyclomatic Complexity analysis tool

    2. Look at the largest Cyclomatic Complexity number and refactor.

    CyclomaticAnalysis.gif
    Figure: Results from Cyclomatic analysis these metrics give an indication on how complicated functions are
    ​Tip: Maintainability index > 85 is good and < 65 is hard to maintain
  13. Do you run Unit Tests in Visual Studio?

    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.
    test-explorer.jpg
    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.

    run-tests.jpg
    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.

    Tip: If you are using dotnet Core/5+ you can do this from the terminal by running dotnet test

  14. 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 static 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. In Functional Programming this is called a "Pure Function". 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 static 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 a unit test for complex logic evaluation​
  15. Do you know Microsoft’s recommended frameworks for Automated UI-driven Functional Testing?

    This type of testing runs the whole application and uses tools to interact with the application in the same way that a user would – such as clicking buttons or entering text into an input field.

    This type of testing is powerful as it tests the entire application, including the UI, and is especially useful for mission-critical pathways such as a shopping cart checkout process.

    The downside of this type of test is that it can be complex to write and that the tests can sometimes be brittle – small changes to the UI can break your tests.

    Because these tests run on top of your UI, the type of UI drives the choice of the testing framework.


    ​Coded UI Tests – Deprecated​​

    Visual Studio 2019 will be the last version of visual studio that supports coded UI tests so this should only be considered if you already have significant investment in existing coded UI tests.​

    Coded UI tests could test Web, Winforms, WPF and Silverlight applications.​

    ​​Web Applications: Selenium

    Selenium works by automating control of a web browser and running it against a deployed website. It is the recommended approach for testing web applications.

    ​​Desktop and UWP: Appium with WinAppDriver

    The Windows Application driver installs a service onto a Windows 10 machine. This service allows you to write test scripts that can launch and interact with windows applications.

    Android and IOS: Xam​​arin.UITest​​

    Xamarin.UITest runs on top of the NUnit unit test framework and can test mobile applications. It inte​grates tightly with Xamarin.iOS and Xamarin.Android projects to test Xamarin-based apps but can also test native applications.

  16. Do you have tests for Performance?

    ​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 automated with your unit testing framework.

    ​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​
  17. Do you have a /HealthCheck (was /zsValidate) page to test your website dependencies?

    There are two kinds of errors, coding errors, and system health errors. Coding errors should be found during development by compiling, debugging, or running unit tests. While system health errors should be found by application health checks.

    ​Refer to the following rules for details:

  18. Do you isolate your logic from your IO to increase the testability?

    If your method is consists of logic and IO, we recommend you isolate 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​
  19. Do you reference the issue ID when writing a test to confirm a bugfix?

    Some bugs have a whole histroy related to them and when we fix them we don't want to lose the rationale for the test. By adding a comment to the test, referencing the bug ID - future developers can see why a test is testing a particular behaviour.

    ​[Test]
    public void TestProj11()
    {
    }

    Figure: Bad example - The test name is the bug ID and I don't know what it is meant to test​​​

    ///
    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 TestResavingAfterPressingBackShouldntBreak()
    {
    }

    Figure: Good example - The test name is clearer, good comments for the unit test​​​ give a little context, and there is a link to original bug
  20. 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:


  21. Do you use Live Unit Testing to see code coverage?

    Visual Studio 2017 introduces a new feature called Live Unit Testing. This gives the developer insight into code coverage of the file that they are working on, so they can quickly and easily see if there’s a unit test that covers the code they are working on.
    lut-codecoverage1.jpg
    Figure: Enable it by selecting Test | Live Unit Testing | Start
    lut-codecoverage2.jpg
    Figure: Bad Example – This method isn't covered by any unit tests, so the developer should consider writing a unit test for it
    lut-codecoverage3.jpg
    Figure: The developer can right click and create a test immediately
    lut-codecoverage4.jpg
    Figure: Good Example – Developer can see that the code is covered by 2 passing tests and one failing test

    For more details see Joe Morris’s video on .NET Tooling Improvements Overview – Live Unit Testing:

    ​​

  22. 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.
    testURLSettings.gif
    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;
    }
    }


  23. 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:​

    • Stored Procedures
    • Triggers
    • User-defined functions
    • Views​
    These tests can also be added to the same library as your unit, web and load tests.
    AddNewTest.jpg
    Figure 1 - Database Unit Test
    WriteUnitTest.jpg
    Figure 2 - Writing the unit test against a stored proc​
  24. Do you use IntelliTesting to save you in testing?

    It is difficult to measure test quality as there are a number of different available metrics - for example, code coverage and number of assertions. Furthermore, when we write code to test, there are a number of questions that we must answer, such as, "is the code easily testable?" and "are we only testing the happy path or have we included the edge cases?"​

    However, the most important question a dev can ask themselves is, "What assertions should I test?​".

    This is where IntelliTesting comes into play. The feature, formerly known as Smart Unit Testing (and even more formerly known as PEX), will help you answer this question by intelligently analyzing your code. Then, based on the information gathered, it will generate a unit test for each scenario it finds.

    IntelliTest-bad.png
    Figure: Bad example - What’s wrong with this code?
    IntelliTest-Good.png
    Figure: Good example - IntelliTest in action​

    In short, by using IntelliTest, you will increase code coverage, greatly increase the number of assertions tested, and increase the number of edge cases tested. By adding automation to your testing, you save yourself time in the long run and reduce the risk of problems in your code caused by simple human error.

  25. Do you know the best test framework to run your integration tests?

    Both NUnit and xUnit are great choices for unit testing – and are highly recommended. Both these frameworks are optimized for unit testing - and xUnit, in particular, has been designed to encourage strong unit test principles by keeping tests isolated.​

    ​When it comes to writing integration tests, you often write tests against slower shared resources and you need more flexibility on how to discover, set up and run your tests.

    Fixie solves this issue by providing an extensible conventions based system to control how tests are discovered and executed.

    • You can switch from the default frequent instance-per-test test class construction (xUnit-style) to infrequent shared class instance (NUnit style)
    • You can configure async setup methods to manage expensive dependencies
    • This configuration is via conventions to keep your testing code concise
    • In fixie, tests don't run in parallel – which is more suitable for integration tests over shared resources

    Read the Fixie Documentation here: https://github.com/fixie/fixie/wiki