PM> Install-Package nspec
PM> Install-Package FluentAssertions
my_first_spec.cs
and put the following code in it:Then run the tests using NSpecRunner.exe
:
PM> NSpecRunner.exe YourClassLibraryProject\bin\debug\YourClassLibraryProject.dll
my first spec
asserts at the method level
describe nesting
asserts in a method
more nesting
also asserts in a lambda
If you’re not in Package Manager Console, but just in a regular command or PowerShell prompt, move to your solution directory and invoke runner by its full path:
> packages\NSpec.x.y.z\tools\net451\win7-x64\NSpecRunner.exe YourClassLibraryProject\bin\debug\YourClassLibraryProject.dll
Please see Targeting .NET Core section down below.
If you’ve used any of the following testing frameworks, you’ll feel right at home with NSpec:
In NSpec, there is no need for access modifiers on tests, and no need to decorate test methods with attributes.
For example, this NUnit/XUnit test:
Would be written like this in NSpec (notice that there are no access modifiers or attributes):
You can nest a lambda within a method (you can defer inheritance hierarchies):
Here is an NSpec test that has nested structures:
Here is what you’d have to write the test above in NUnit/XUnit. It’s gross and poopy. Specifically:
Lets take a look at some features of NSpec.
NSpec has some simple assertions, but you should really just use FluentAssertions, or Shouldly, or another assertion framework. You can build your own assertions by using extension methods. For example:
Want to do some setup before tests are run? Use before
. The state of
the class is reset with each test case (side effects/mutations don’t spill over).
Test hierarchies are communicated through the context
keyword. If a
method contains underscores, then it will be picked up by NSpec. Any
method that starts with it_
or specify_
will be treated as just a
simple NUnit/XUnit style test case.
When your code should throw under specific conditions, you can assert that it does so with an expected Exception of known type and message.
class describe_exceptions : nspec
{
void when_code_throws()
{
before = () => { };
it["throws expected exception type"] = expect<KnownException>(() => { throw new KnownException(); });
it["throws expected exception type and error message"] =
expect<KnownException>("My message", () => { throw new KnownException("My message"); });
}
}
describe exceptions
when code throws
throws expected exception type
throws expected exception type and error message
2 Examples, 0 Failed, 0 Pending
You can ignore tests by preceding any structure with an x
. Or you can
use the todo
keyword provided by NSpec.
Title cased (conventional C#) methods are ignored my NSpec.
Here’s a fancy feature. Sometimes, what is done to a class remains the
same, but the setup varies. You can use act
. Each nested
context will execute act
before assertions are run.
Being able to nest tests is awesome. But you can always use inheritance to “flatten” tests if needed.
All test structures are supported at the class level too. Here is how
you’d write a before
, act
, and it/specify
at the class level.
If you want to hook into the debugger quickly, just place the
following line inside of your tests. When you run NSpecRunner.exe
,
the debugger will pop right up:
System.Diagnostics.Debugger.Launch()
NSpec also includes DebuggerShim.cs
when you install it via
Nuget. So you can use TDD.NET/ReSharper to run your tests.
Or you can do something even fancier, and build your own console
app! Instead of creating a Class Library for the test project,
create a Console Application. Add the following code in Program.cs
:
Then you can debug everything like you would any other program. More importantly, creating your own console app gives you the power to tailor input and output to your liking using NSpecs’s API/constructs.
Your NSpec tests can run asynchronous code too.
At a class level, you still declare hook methods with same names, but
they must be asynchronous and return async Task
, instead of
void
:
For all sync test hooks at class level you can find its corresponding async one, just by turning its signature to async:
Sync | Async |
---|---|
void before_all() |
async Task before_all() |
void before_each() |
async Task before_each() |
void act_each() |
async Task act_each() |
void it_xyz() |
async Task it_xyz() |
void specify_xyz() |
async Task specify_xyz() |
void after_each() |
async Task after_each() |
void after_all() |
async Task after_all() |
Throughout the test class you can run both sync and async expectations
as needed, so you can freely mix void it_xyz()
and async Task
it_abc()
.
Given a class context, for each test execution phase (before all/
before/ act/ after/ after all) you can choose to run either
sync or async code according to your needs: so in the same class
context you can mix e.g. void before_all()
with async Task
before_each()
, void act_each()
and async Task after_each()
.
What you can’t do is to assign both sync and async hooks for the same phase, in the same class context: so e.g. the following will not work and break your build at compile time (for the same rules of method overloading):
At a context and sub-context level, you need to set asynchronous test hooks provided by NSpec, instead of the synchronous ones:
For almost all sync test hooks and helpers you can find its corresponding async one:
Sync | Async |
---|---|
beforeAll |
beforeAllAsync |
before |
beforeAsync |
beforeEach |
beforeEachAsync |
act |
actAsync |
it |
itAsync |
xit |
xitAsync |
expect |
expectAsync |
todo |
todoAsync |
after |
afterAsync |
afterEach |
afterEachAsync |
afterAll |
afterAllAsync |
specify |
Not available |
xspecify |
Not available |
context |
Not needed, context remains sync |
xcontext |
Not needed, context remains sync |
describe |
Not needed, context remains sync |
xdescribe |
Not needed, context remains sync |
Throughout the whole test class you can run both sync and async
expectations as needed, so you can freely mix it[]
and itAsync[]
.
Given a single context, for each test execution phase (before all/
before/ act/ after/ after all) you can choose to run either
sync or async code according to your needs: so in the same context you
can mix e.g. beforeAll
with beforeAsync
, act
and afterAsync
.
What you can’t do is to assign both sync and async hooks for the same phase, in the same context: so e.g. the following will not work and throw an exception at runtime:
If you want to dig deeper for any level, whether class- or context-, you might directly have a look at how async support is tested in NSpec unit tests.
Just look for nspec
-derived classes in following files:
Test frameworks of the xUnit family have dedicated attributes in order to support data-driven test cases (so-called theories). NSpec, as a member of the xSpec family, does not make use of attributes and instead obtains the same result with a set of expectations automatically created through code. In detail, to set up a data-driven test case with NSpec you just:
Any NSpec test runner will be able to detect all the (aptly) named
expectations and run them. Here you can see a sample test case, where
we took advantage of NSpec.Each<>
class and NSpec.Do()
extension
to work more easily with data point enumeration, and NSpec.With()
extension to have an easier time composing text:
Please have a look at
this wiki page
for an overview on which test hooks are executed when: execution order
in xSpec family frameworks can get tricky when dealing with more
complicated test configurations, like inherithing from an abstract
test class or mixing before_each
with before_all
at different
context levels.
Currently with NSpec out-of-the-box you can only expect that some code
under test throws, but you cannot at the same time perform any further
assertion, e.g. that some flag has been set or resource disposed in a
finally
block.
One workaround to this scenario is to add a custom helper like the following to your test class:
class describe_expect_and_assert : nspec
{
void ExpectAndAssert<TEx>(string testName,
Action actionThatThrows,
Action<TEx> assertAfter)
where TEx : Exception
{
it[testName] = () =>
{
bool exceptionThrown = false;
try
{
actionThatThrows();
}
catch(TEx ex)
{
exceptionThrown = true;
assertAfter(ex);
}
if(!exceptionThrown) throw new InvalidOperationException("Exception was not thrown when expected");
};
}
// ...
}
and then use it to both expect some specific Exception type to be thrown, as well as asserting some further expectation:
class describe_expect_and_assert : nspec
{
// before_each ...
void a_context()
{
// add a specification
ExpectAndAssert<KnownException>("throws an exception",
() => someObject.DoSomething(),
ex => someObject.Flag.should_be(true));
// add another specification
ExpectAndAssert<OtherKnownException>("throws another exception",
() => someObject.DoSomethingElse(),
ex => someObject.OtherFlag.should_be(true));
}
}
Besides targeting classic .NET Framework, NSpec supports writing tests
for projects targeting .NET Core too. That means you can also run tests
from console with dotnet test
Command Line Interface.
The following setup holds for projects based on .xproj
and
project.json
, currently working with .NET Core 1.0 and .NET Core Tooling
Preview 2.
As of today this scenario is deemed to become obsolete, once
.NET Core Tooling reaches RTM with projects based back again on .csproj
and MSBuild.
To setup test project you can proceed from scratch, or take advantage of
xUnit template and start modifying from there. Either way, the final
result is to have a project.json
as the following (also targeting
net451
):
{
"version": "1.0.0-*",
"testRunner": "nspec",
"dependencies": {
"dotnet-test-nspec": "0.2.0",
"LibraryUnderTest": {
"target": "project"
},
"NSpec": "3.0.0",
"Shouldly": "2.8.2"
},
"frameworks": {
"netcoreapp1.0": {
"imports": [
"portable-net45+win8"
],
"dependencies": {
"Microsoft.Extensions.Testing.Abstractions": "1.0.0-preview2-003121",
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0"
}
}
},
"net451": {
}
}
}
dotnet new --install dotnet-new-nspec::*
dotnet new nspectest -n MyTestProject
NOTE: Even if template currently creates a project supporting .NET Core Tools Preview 2 (the one based on project.json), in order to install custom CLI templates like this you need to have at least .NET Core Tools RC4 on your development machine.
dotnet new -t xunittest
(when you have .NET Core Tools Preview 2)
or dotnet new xunit -n MyTestProject
(when you have .NET Core Tools RC4)xunit
NuGet package dependency in project.json
with
NSpec
dotnet-test-xunit
NuGet package dependency in project.json
with dotnet-test-nspec
xunit
value of testRunner
property in project.json
with
nspec
Microsoft.Extensions.Testing.Abstractions
buildOptions.emitEntryPoint
property from project.json
NSpec
NuGet packagedotnet-test-nspec
NuGet packagetestRunner
property set to nspec
in project.json
Microsoft.Extensions.Testing.Abstractions
Program.cs
fileWhichever way you choose, project is now setup. From a command line
located at test project directory, run dotnet restore
. Add your test
class, like the one shown at the top of this page,
then from the same command line run:
> dotnet build
> dotnet test
or, from within Visual Studio 2015, build solution then open Test Explorer window in order to list and run tests.
See under examples/ in GitHub repository:
DotNetTestSample
Sample solution showing how to setup a NSpec test project targeting .NET Core
NetFrameworkSample
Sample solution showing how to setup a NSpec test project targeting .NET Framework
NSpec examples can be run as NUnit tests from inside Visual Studio (using for example the ReSharper test runner) or on a CI server using the NUnit console runner. To do this, install the NSpecInNUnit package and extend a special base class. Full usage instructions are at the project site for NSpecInNUnit.
NSpec.VsAdapter is a test adapter to run NSpec tests from Test Explorer in Visual Studio. It runs tests in VS 2013, 2015 and 2017 from projects targeting classic .NET Framework.
It is available both as a Visual Studio Extension as well as a NuGet Package.
Please check project home for further details and instructions.