Walkthrough: Create and run unit tests for UWP apps

This article describes how to unit test Universal Windows Platform (UWP) apps in Visual Studio. Visual Studio offers UWP unit test project templates for C#, Visual Basic, and C++. For more information about developing UWP apps, see Get started with UWP apps.

The article walks through an example of creating and unit testing a C# class in a UWP app. The example uses test-driven development to write tests that verify specific behaviors, and then write code that passes the tests.

Create and run a unit test project

The following procedures describe how to create and run unit test projects for UWP apps.

Create a UWP unit test project

  1. On the Visual Studio Start window, choose Create a new project.

  2. On the Create a new project page, enter unit test in the Search box. The template list filters to unit testing projects.

  3. Select the Unit Test App (Universal Windows) template for either C# or Visual Basic, and then select Next.

    Screenshot that shows creating a new UWP unit test app in Visual Studio.

  4. Optionally change the project or solution name and location, and then select Create.

  5. Optionally change the target and minimum platform versions, and then select OK.

Visual Studio creates the test project and opens it in Visual Studio Solution Explorer.

Screenshot that shows the UWP unit test project in Solution Explorer.

  1. On the Visual Studio Start window, choose Create a new project.

  2. On the Create a new project page, enter unit test in the Search box. The template list filters to unit testing projects.

  3. Select the Unit Test App (Universal Windows) template for either C# or Visual Basic, and then select Next.

    Screenshot that shows creating a new UWP unit test app in Visual Studio.

  4. Optionally change the project or solution name and location, and then select Create.

  5. Optionally change the target and minimum platform versions, and then select OK.

Visual Studio creates the test project and opens it in Visual Studio Solution Explorer.

Screenshot that shows the UWP unit test project in Solution Explorer.

Edit the project's application manifest

  1. In Solution Explorer, right-click the Package.appxmanifest file and select Open.

  2. In the manifest designer, select the Capabilities tab.

  3. In the Capabilities list, select the capabilities you need for the code and unit test. For example, if your code and its unit test need to access the internet, select the Internet checkbox.

Select only the capabilities you need for the unit test to function correctly.

Screenshot that shows a unit test manifest.

Screenshot that shows a unit test manifest.

Add code to unit test the UWP app

In the Visual Studio code editor, edit the unit test code file to add the asserts and logic your tests require. For examples, see Unit test a C# class later in this article.

Run the unit test with Test Explorer

Build the solution and run the unit test by using Test Explorer.

  1. On the Visual Studio Test menu, select Test Explorer. The Test Explorer window opens.

  2. In Test Explorer, select the Run all icon. You must use Run all to discover tests in UWP projects.

    Screenshot that shows Test Explorer Run all icon.

The solution builds and the unit test runs. After the test runs, the test appears in the Test Explorer test list, with information about outcome and duration.

Screenshot that shows the Test Explorer with completed test information.

Also in Test Explorer, you can select individual tests and right-click to Run or Debug the tests, or Go To Test to open the test code. From the top menu, you can group tests, add tests to playlists, or open test options.

Screenshot that shows a Test Explorer context menu.

Build the solution and run the unit test by using Test Explorer.

  1. On the Visual Studio Test menu, select Test Explorer. The Test Explorer window opens.

  2. In Test Explorer, select the Run all icon. You must use Run all to discover tests in UWP projects.

    Screenshot that shows Test Explorer Run all icon.

The solution builds and the unit test runs. After the test runs, the test appears in the Test Explorer test list, with information about outcome and duration.

Screenshot that shows the Test Explorer with completed test information.

Also in Test Explorer, you can select individual tests and right-click to Run or Debug the tests, or Go To Test to open the test code. From the top menu, you can group tests, add tests to playlists, or open test Options.

Screenshot that shows a Test Explorer context menu.

Unit test a C# class

A stable set of good unit tests increases confidence that you haven't introduced bugs when you change code. The following example walks through one way to create unit tests for a C# class in a UWP app. The example uses test-driven development to write tests that verify specific behavior, and then write code that passes the tests.

In the example Maths code project, the Rooter class implements a function that calculates the estimated square root of a number. The RooterTests project unit tests the Rooter class.

Create the solution and projects

Create the UWP app project:

  1. On the Visual Studio File menu, select New Project.
  2. On the Create a new project page, enter blank app in the Search box, and then select the C# Blank App (Universal Windows) project template.
  3. On the Configure your new project page, name the project Maths, and select Create.
  4. Optionally change the target and minimum platform versions, and then select OK. Visual Studio creates the project and opens it in Solution Explorer.

Create the unit test project:

  1. In Solution Explorer, right-click the Maths solution and choose Add > New Project.
  2. On the Add a new project page, enter unit test in the Search box, and then select the C# Unit Test App (Universal Windows) project template.
  3. Name the test project RooterTests, and select Create.
  4. Optionally change the target and minimum platform versions, and then select OK. The RooterTests project appears under the Maths solution in Solution Explorer.

Verify that tests run in Test Explorer

The Assert class provides several static methods that you can use to verify results in test methods.

  1. In Solution Explorer, select the UnitTest.cs file in the RooterTests project.

  2. Insert the following code into TestMethod1:

    [TestMethod]
    public void TestMethod1()
    {
        Assert.AreEqual(0, 0);
    }
    
  3. In Test Explorer, select Run All Tests.

  4. The test project builds and runs, and the test appears under Passed Tests. The Group Summary pane on the right provides details about the test.

Add a class to the app project

  1. In Solution Explorer, right-click the Maths project and select Add > Class.

  2. Name the class file Rooter.cs, and then select Add.

  3. In the code editor, add the following code to the Rooter class in the Rooter.cs file:

    public Rooter()
    {
    }
    
    // estimate the square root of a number
    public double SquareRoot(double x)
    {
        return 0.0;
    }
    

    The Rooter class declares a constructor and the SquareRoot estimator method. The SquareRoot method is a minimal implementation to test the basic testing setup.

  4. Change the internal keyword to public in the Rooter class declaration, so the test code can access it.

    public class Rooter
    
  1. In Solution Explorer, right-click the Maths project and select Add > Class.

  2. Name the class file Rooter.cs, and then select Add.

  3. In the code editor, add the following code to the Rooter class in the Rooter.cs file:

    public Rooter()
    {
    }
    
    // estimate the square root of a number
    public double SquareRoot(double x)
    {
        return 0.0;
    }
    

    The Rooter class declares a constructor and the SquareRoot estimator method. The SquareRoot method is a minimal implementation to test the basic testing setup.

  4. Add the public keyword to the Rooter class declaration, so the test code can access it.

    public class Rooter
    

Add a reference from the test project to the app project

  1. In Solution Explorer, right-click the RooterTests project, and select Add > Reference.

  2. In the Reference Manager - RooterTests dialog box, expand Projects, and select the Maths project.

    Screenshot that shows adding a reference to the Maths project.

  3. Select OK.

  4. Add the following using statement to the UnitTest.cs, after the using Microsoft.VisualStudio.TestTools.UnitTesting; line:

    using Maths;
    
  1. In Solution Explorer, right-click the RooterTests project, and select Add > Reference.

  2. In the Reference Manager - RooterTests dialog box, expand Projects, and select the Maths project.

    Screenshot that shows adding a reference to the Maths project.

  3. Select OK.

  4. Add the following using statement to UnitTest.cs, after the using Microsoft.VisualStudio.TestTools.UnitTesting; line:

    using Maths;
    

Add a test that uses the app function

  1. Add the following test method to UnitTest.cs:

    [TestMethod]
    public void BasicTest()
    {
        Maths.Rooter rooter = new Rooter();
        double expected = 0.0;
        double actual = rooter.SquareRoot(expected * expected);
        double tolerance = .001;
        Assert.AreEqual(expected, actual, tolerance);
    }
    

    The new test appears in Solution Explorer and in the Not Run Tests node of Test Explorer.

  2. To avoid a "Payload contains two or more files with the same destination path" error, in Solution Explorer, expand the Properties node under the Maths project, and delete the Default.rd.xml file.

  3. Save all files.

Run the tests

In Test Explorer, select the Run All Tests icon. The solution builds, and the tests run and pass.

Screenshot that shows Basic Test passed in Test Explorer

In Test Explorer, select the Run All Tests icon. The solution builds, and the tests run and pass.

Screenshot that shows Basic Test passed in Test Explorer

If you get a Duplicate Entity error when you run the test, delete the runtime directives file, Properties\Default.rd.xml from the test project and re-try.

You've set up the test and app projects and verified that you can run tests that call functions in the app project. Now you can write real tests and code.

Add tests and make them pass

It's best not to change tests that have passed. Add new tests instead. Develop code by adding tests one at a time, and make sure that all tests pass after each iteration.

  1. Add a new test called RangeTest to UnitTest.cs:

    [TestMethod]
    public void RangeTest()
    {
        Rooter rooter = new Rooter();
        for (double v = 1e-6; v < 1e6; v = v * 3.2)
        {
            double expected = v;
            double actual = rooter.SquareRoot(v*v);
            double tolerance = expected/1000;
            Assert.AreEqual(expected, actual, tolerance);
        }
    }
    
  2. Run the RangeTest test and verify that it fails.

    Screenshot that shows the RangeTest fails in Test Explorer.

    Tip

    In test driven development, you run a test immediately after you write it. This practice helps you avoid the easy mistake of writing a test that never fails.

  3. Fix your app code so that the new test passes. In Rooter.cs, change the SquareRoot function as follows:

    public double SquareRoot(double x)
    {
        double estimate = x;
        double diff = x;
        while (diff > estimate / 1000)
        {
            double previousEstimate = estimate;
            estimate = estimate - (estimate * estimate - x) / (2 * estimate);
            diff = Math.Abs(previousEstimate - estimate);
        }
        return estimate;
    }
    
  4. In Test Explorer, select the Run all tests icon. All three tests now pass.

  1. Add a new test called RangeTest to UnitTest.cs:

    [TestMethod]
    public void RangeTest()
    {
        Rooter rooter = new Rooter();
        for (double v = 1e-6; v < 1e6; v = v * 3.2)
        {
            double expected = v;
            double actual = rooter.SquareRoot(v*v);
            double tolerance = expected/1000;
            Assert.AreEqual(expected, actual, tolerance);
        }
    }
    
  2. Run the RangeTest test and verify that it fails.

    Screenshot that shows the RangeTest fails in Test Explorer.

    Tip

    In test driven development, you run a test immediately after you write it. This practice helps you avoid the easy mistake of writing a test that never fails.

  3. Fix your app code so that the new test passes. In Rooter.cs, change the SquareRoot function as follows:

    public double SquareRoot(double x)
    {
        double estimate = x;
        double diff = x;
        while (diff > estimate / 1000)
        {
            double previousEstimate = estimate;
            estimate = estimate - (estimate * estimate - x) / (2 * estimate);
            diff = Math.Abs(previousEstimate - estimate);
        }
        return estimate;
    }
    
  4. In Test Explorer, select the Run all tests icon. All three tests now pass.

Refactor the code

In this section, you refactor both the app and the test code, then rerun the tests to make sure they still pass.

Simplify the square root estimation

  1. In Rooter.cs, simplify the central calculation in the SquareRoot function by changing the following line:

    estimate = estimate - (estimate * estimate - x) / (2 * estimate);

    To

    estimate = (estimate + x/estimate) / 2.0;

  2. Run all tests to make sure you haven't introduced a regression. The tests should all pass.

Remove duplicate test code

The RangeTest method hard codes the denominator of the tolerance variable that's passed to the Assert method. If you plan to add more tests that use the same tolerance calculation, using a hard-coded value in several locations makes the code harder to maintain. Instead, you can add a private helper method to the UnitTest1 class to calculate the tolerance value, and then call that method from RangeTest.

To add the helper method, in UnitTest.cs:

  1. Add the following method to the UnitTest1 class:

    private double ToleranceHelper(double expected)
    {
        return expected / 1000;
    }
    
  2. In RangeTest, change the following line:

    double tolerance = expected/1000;

    To

    double tolerance = ToleranceHelper(expected);

  3. Run the RangeTest test to make sure it still passes.

Tip

If you add a helper method to a test class, and you don't want the helper method to appear in the list in Test Explorer, don't add the TestMethodAttribute attribute to the method.

Next steps