-
Notifications
You must be signed in to change notification settings - Fork 5.9k
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
Changes from 3 commits
7211509
73b38c8
b30c992
5bff426
82eb246
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be an H2 |
||
|
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
} | ||
|
||
#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> |
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 |
There was a problem hiding this comment.
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.There was a problem hiding this comment.
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