Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds NUnit test tutorial for .NET Core C# #3856

Merged
merged 5 commits into from
Dec 5, 2017
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/core/testing/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ When writing unit tests, be careful you don’t accidentally introduce dependenc

Learn more about unit testing in .NET Core projects:

Unit Test projects for .NET Core are supported for [C#](../../csharp/index.md), [F#](../../fsharp/index.md) and [Visual Basic](../../visual-basic/index.md). You can also choose between [xUnit](http://xunit.github.io) and [MSTest](https://github.com/Microsoft/vstest-docs).
Unit Test projects for .NET Core are supported for [C#](../../csharp/index.md), [F#](../../fsharp/index.md) and [Visual Basic](../../visual-basic/index.md). You can also choose between [xUnit](http://xunit.github.io), [NUnit](http://nunit.org) and [MSTest](https://github.com/Microsoft/vstest-docs).

You can read about those combinations in these walkthroughs:

* Create unit tests using [*XUnit* and *C#* with the .NET Core CLI](unit-testing-with-dotnet-test.md).
* Create unit tests using [*NUnit* and *C#* with the .NET Core CLI](unit-testing-with-nunit.md).
* Create unit tests using [*MSTest* and *C#* with the .NET Core CLI](unit-testing-with-mstest.md).
* Create unit tests using [*XUnit* and *F#* with the .NET Core CLI](unit-testing-fsharp-with-dotnet-test.md).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, dotnet new nunit can generate projects for F# and Visual Basic too. You can use -lang F# or -lang VB switches to the command for that. Just like in docs for XUnit and MSTest. Consider to add two more pages for these languages too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking a look @halex2005. I have submitted tutorials for F# and VB separately, see #3858 and #3859

* Create unit tests using [*MSTest* and *F#* with the .NET Core CLI](unit-testing-fsharp-with-mstest.md).
Expand Down
167 changes: 167 additions & 0 deletions docs/core/testing/unit-testing-with-nunit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
title: Unit testing C# with NUnit and .NET Core
description: Learn unit test concepts in C# and .NET Core through an interactive experience building a sample solution step-by-step using dotnet test and NUnit.
keywords: NUnit, .NET, .NET Core
author: rprouse
ms.date: 12/01/2017
ms.topic: article
ms.prod: .net-core
ms.devlang: dotnet
ms.assetid: 9a978eef-3660-4b8f-bced-4620ff392476
---

# Unit testing C# with NUnit and .NET Core

This tutorial takes you through an interactive experience building a sample solution step-by-step to learn unit testing concepts. If you prefer to follow the tutorial using a pre-built solution, [view or download the sample code](https://github.com/dotnet/docs/blob/master/samples/core/getting-started/unit-testing-using-nunit/) before you begin. For download instructions, see [Samples and Tutorials](../../samples-and-tutorials/index.md#viewing-and-downloading-samples).

### Creating the source project
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be an H2 ## instead of H3 ###


Open a shell window. Create a directory called *unit-testing-using-nunit* to hold the solution. Inside this new directory, run [`dotnet new sln`](../tools/dotnet-new.md) to create
a new solution file for the class library and the test project. Next, create a *PrimeService* directory. The following outline shows the directory and file structure thus far:

```
/unit-testing-using-nunit
unit-testing-using-nunit.sln
/PrimeService
```

Make *PrimeService* the current directory and run [`dotnet new classlib`](../tools/dotnet-new.md) to create the source project. Rename *Class1.cs* to *PrimeService.cs*. To use test-driven development (TDD), you create a failing implementation of the `PrimeService` class:

```csharp
using System;

namespace Prime.Services
{
public class PrimeService
{
public bool IsPrime(int candidate)
{
throw new NotImplementedException("Please create a test first");
}
}
}
```

Change the directory back to the *unit-testing-using-nunit* directory. Run [`dotnet sln add PrimeService/PrimeService.csproj`](../tools/dotnet-sln.md) to add the class library project to the solution.

### Install the NUnit project template
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also H2


The NUnit test project templates need to be installed before creating a test project. This only needs to be done once. Run [`dotnet new -i NUnit3.DotNetNew.Template`](../tools/dotnet-new.md) to install the NUnit templates.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's clarify that this is a per machine install. Meaning the command needs to be done once on each development machine where you'll create new NUnit projects.


```
dotnet new -i NUnit3.DotNetNew.Template
```

### Creating the test project

Next, create the *PrimeService.Tests* directory. The following outline shows the directory structure:

```
/unit-testing-using-nunit
unit-testing-using-nunit.sln
/PrimeService
Source Files
PrimeService.csproj
/PrimeService.Tests
```

Make the *PrimeService.Tests* directory the current directory and create a new project using [`dotnet new nunit`](../tools/dotnet-new.md). The dotnet new command creates a test project that uses NUnit as the test library. The generated template configures the test runner in the *PrimeServiceTests.csproj* file:

```xml
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
<PackageReference Include="NUnit" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.9.0" />
</ItemGroup>
```

The test project requires other packages to create and run unit tests. `dotnet new` in the previous step added the Microsoft test SDK, the NUnit test framework, and the NUnit test adapter. Now, add the `PrimeService` class library as another dependency to the project. Use the [`dotnet add reference`](../tools/dotnet-add-reference.md) command:

```
dotnet add reference ../PrimeService/PrimeService.csproj
```

You can see the entire file in the [samples repository](https://github.com/dotnet/docs/blob/master/samples/core/getting-started/unit-testing-using-nunit/PrimeService.Tests/PrimeService.Tests.csproj) on GitHub.

The following outline shows the final solution layout:

```
/unit-testing-using-nunit
unit-testing-using-nunit.sln
/PrimeService
Source Files
PrimeService.csproj
/PrimeService.Tests
Test Source Files
PrimeServiceTests.csproj
```

Execute [`dotnet sln add .\PrimeService.Tests\PrimeService.Tests.csproj`](../tools/dotnet-sln.md) in the *unit-testing-using-dotnet-test* directory.

## Creating the first test

The TDD approach calls for writing one failing test, making it pass, then repeating the process. Remove *UnitTest1.cs* from the *PrimeService.Tests* directory and create a new C# file named *PrimeService_IsPrimeShould.cs* with the following content:

```csharp
using NUnit.Framework;
using Prime.Services;

namespace Prime.UnitTests.Services
{
[TestFixture]
public class PrimeService_IsPrimeShould
{
private readonly PrimeService _primeService;

public PrimeService_IsPrimeShould()
{
_primeService = new PrimeService();
}

[Test]
public void ReturnFalseGivenValueOf1()
{
var result = _primeService.IsPrime(1);

Assert.IsFalse(result, "1 should not be prime");
}
}
}
```

The `[TestFixture]` attribute denotes a class that contains unit tests. The `[Test]` attribute indicates a method is a test method.

Save this file and execute [`dotnet test`](../tools/dotnet-test.md) to build the tests and the class library and then run the tests. The NUnit test runner contains the program entry point to run your tests. `dotnet test` starts the test runner using the unit test project you've created.

Your test fails. You haven't created the implementation yet. Make this test pass by writing the simplest code in the `PrimeService` class that works:

```csharp
public bool IsPrime(int candidate)
{
if (candidate == 1)
{
return false;
}
throw new NotImplementedException("Please create a test first");
}
```

In the *unit-testing-using-nunit* directory, run `dotnet test` again. The `dotnet test` command runs a build for the `PrimeService` project and then for the `PrimeService.Tests` project. After building both projects, it runs this single test. It passes.

## Adding more features

Now that you've made one test pass, it's time to write more. There are a few other simple cases for prime numbers: 0, -1. You could add new tests with the `[Test]` attribute, but that quickly becomes tedious. There are other NUnit attributes that enable you to write a suite of similar tests. A `[TestCase]` attribute is used to create a suite of tests that execute the same code but have different input arguments. You can use the `[TestCase]` attribute to specify values for those inputs.

Instead of creating new tests, apply this attribute to create a single data driven test. The data driven test is a method that tests several values less than two, which is the lowest prime number:

[!code-csharp[Sample_TestCode](../../../samples/core/getting-started/unit-testing-using-nunit/PrimeService.Tests/PrimeService_IsPrimeShould.cs?name=Sample_TestCode)]

Run `dotnet test`, and two of these tests fail. To make all of the tests pass, change the `if` clause at the beginning of the method:

```csharp
if (candidate < 2)
```

Continue to iterate by adding more tests, more theories, and more code in the main library. You have the [finished version of the tests](https://github.com/dotnet/docs/blob/master/samples/core/getting-started/unit-testing-using-nunit/PrimeService.Tests/PrimeService_IsPrimeShould.cs) and the [complete implementation of the library](https://github.com/dotnet/docs/blob/master/samples/core/getting-started/unit-testing-using-nunit/PrimeService/PrimeService.cs).

You've built a small library and a set of unit tests for that library. You've structured the solution so that adding new packages and tests is part of the normal workflow. You've concentrated most of your time and effort on solving the goals of the application.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="nunit" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\PrimeService\PrimeService.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using NUnit.Framework;
using Prime.Services;

namespace Prime.UnitTests.Services
{
[TestFixture]
public class PrimeService_IsPrimeShould
{
private readonly PrimeService _primeService;

public PrimeService_IsPrimeShould()
{
_primeService = new PrimeService();
}

[Test]
public void ReturnFalseGivenValueOf1()
{
var result = _primeService.IsPrime(1);

Assert.IsFalse(result, $"1 should not be prime");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The $ is superfluous here. Let's remove it.

}

#region Sample_TestCode
[TestCase(-1)]
[TestCase(0)]
[TestCase(1)]
public void ReturnFalseGivenValuesLessThan2(int value)
{
var result = _primeService.IsPrime(value);

Assert.IsFalse(result, $"{value} should not be prime");
}
#endregion
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;

namespace Prime.Services
{
public class PrimeService
{
public bool IsPrime(int candidate)
{
if (candidate < 2)
{
return false;
}
throw new NotImplementedException("Please create a test first");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

</Project>
22 changes: 22 additions & 0 deletions samples/core/getting-started/unit-testing-using-nunit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Unit testing using NUnit sample

This sample is part of the [unit testing tutorial](https://docs.microsoft.com/dotnet/core/testing/unit-testing-with-nunit) for creating applications with unit tests included. See that topic for detailed steps on the code for this sample.

## Key features

This sample demonstrates creating a library and writing effective unit tests that validate the features in that library. The example provides a service that indicates whether a number is prime.

## Restore and test

To run the tests, navigate to the *PrimeService.Tests* directory and type the following commands:

```
dotnet restore
dotnet test
```

`dotnet restore` restores the packages of both projects.
`dotnet test` builds both projects and runs all of the configured tests.

[!INCLUDE[DotNet Restore Note](~/includes/dotnet-restore-note.md)]

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.10
MinimumVisualStudioVersion = 15.0.26124.0
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PrimeService", "PrimeService\PrimeService.csproj", "{A14B23C8-8407-4D0F-BD61-51158C294C25}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PrimeService.Tests", "PrimeService.Tests\PrimeService.Tests.csproj", "{ABD4B561-111A-40DE-8703-50E416B24748}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A14B23C8-8407-4D0F-BD61-51158C294C25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A14B23C8-8407-4D0F-BD61-51158C294C25}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A14B23C8-8407-4D0F-BD61-51158C294C25}.Debug|x64.ActiveCfg = Debug|Any CPU
{A14B23C8-8407-4D0F-BD61-51158C294C25}.Debug|x64.Build.0 = Debug|Any CPU
{A14B23C8-8407-4D0F-BD61-51158C294C25}.Debug|x86.ActiveCfg = Debug|Any CPU
{A14B23C8-8407-4D0F-BD61-51158C294C25}.Debug|x86.Build.0 = Debug|Any CPU
{A14B23C8-8407-4D0F-BD61-51158C294C25}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A14B23C8-8407-4D0F-BD61-51158C294C25}.Release|Any CPU.Build.0 = Release|Any CPU
{A14B23C8-8407-4D0F-BD61-51158C294C25}.Release|x64.ActiveCfg = Release|Any CPU
{A14B23C8-8407-4D0F-BD61-51158C294C25}.Release|x64.Build.0 = Release|Any CPU
{A14B23C8-8407-4D0F-BD61-51158C294C25}.Release|x86.ActiveCfg = Release|Any CPU
{A14B23C8-8407-4D0F-BD61-51158C294C25}.Release|x86.Build.0 = Release|Any CPU
{ABD4B561-111A-40DE-8703-50E416B24748}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ABD4B561-111A-40DE-8703-50E416B24748}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ABD4B561-111A-40DE-8703-50E416B24748}.Debug|x64.ActiveCfg = Debug|Any CPU
{ABD4B561-111A-40DE-8703-50E416B24748}.Debug|x64.Build.0 = Debug|Any CPU
{ABD4B561-111A-40DE-8703-50E416B24748}.Debug|x86.ActiveCfg = Debug|Any CPU
{ABD4B561-111A-40DE-8703-50E416B24748}.Debug|x86.Build.0 = Debug|Any CPU
{ABD4B561-111A-40DE-8703-50E416B24748}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ABD4B561-111A-40DE-8703-50E416B24748}.Release|Any CPU.Build.0 = Release|Any CPU
{ABD4B561-111A-40DE-8703-50E416B24748}.Release|x64.ActiveCfg = Release|Any CPU
{ABD4B561-111A-40DE-8703-50E416B24748}.Release|x64.Build.0 = Release|Any CPU
{ABD4B561-111A-40DE-8703-50E416B24748}.Release|x86.ActiveCfg = Release|Any CPU
{ABD4B561-111A-40DE-8703-50E416B24748}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5E9E068D-AA46-4AC6-ADD1-0046FB77059C}
EndGlobalSection
EndGlobal