Skip to content

Commit

Permalink
moved code snippets to gists (for now) until we figure out Jekyll cod…
Browse files Browse the repository at this point in the history
…e formatting
  • Loading branch information
Amir Rajan committed Feb 5, 2017
1 parent 0385512 commit b41e060
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 216 deletions.
19 changes: 19 additions & 0 deletions assets/css/site.css
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,22 @@ iframe {
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}

table {
border: solid 1px silver;
padding: 3px;
}

table th {
border: solid 1px silver;
padding: 3px;
}

table td {
border: solid 1px silver;
padding: 3px;
}

td.blob-num {
border: none;
}
327 changes: 111 additions & 216 deletions index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ title:
* [Class Level](#class-level)
* [Debugger Support](#debugger-support)
* [Console App](#console-app)
- [Async/await support](#asyncawait-support)
- [Async/Await Support](#asyncawait-support)
* [Class Level](#class-level-1)
* [Context level](#context-level)
- [Data-driven test cases](#data-driven-test-cases)
Expand Down Expand Up @@ -195,7 +195,8 @@ 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.
NSpec also includes `DebuggerShim.cs` when you install it via
Nuget. So you can use TDD.NET/ReSharper to run your tests.

### Console App ###
<hr />
Expand All @@ -210,242 +211,136 @@ 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.

## Async/await support
## Async/Await Support ##
<hr />

Your NSpec tests can run asynchronous code too.

### Class level

At a class level, you still declare hook methods with same names, but they must be *asynchronous* and return `async Task`, instead of `void`:

```c#
public class an_example_with_async_hooks_at_class_level : nspec
{
// Note the different return type and modifier
async Task before_each()
{
await SetupScenarioAsync();
}

async Task act_each()
{
await DoActAsync();
}

void it_should_do_something()
{
DoSomething();
}

async Task it_should_do_something_async()
{
await DoSomethingAsync();
}

void after_each()
{
CleanupScenario();
}

// ...
}
```

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

```c#
public class a_wrong_example_mixing_async_hooks_at_class_level : nspec
{
// Watch out, this example will not work
void before_each() // this one, together with ...
{
SetupScenario();
}

async Task before_each() // ... this other, will cause an error
{
await SetupScenarioAsync();
}

async Task act_each()
{
await DoActAsync();
}

void it_should_do_something()
{
DoSomething();
}

async Task it_should_do_something_async()
{
await DoSomethingAsync();
}

void after_each()
{
CleanupScenario();
}

// ...
}
```

### Context level

At a context and sub-context level, you need to set _asynchronous_ test hooks provided by NSpec, instead of the synchronous ones:

```c#
public class an_example_with_async_hooks_at_context_level : nspec
{
void given_some_context()
{
// Note the 'Async' suffix
beforeAsync = async () => await SetupScenarioAsync();

it["should do something"] = () => DoSomething();

itAsync["should do something async"] = async () => await DoSomethingAsync();

context["given some nested scenario"] = () =>
{
before = () => SetupNestedScenario();

actAsync = async () => await DoActAsync();

itAsync["is not yet implemented async"] = todoAsync;

afterAsync = async () => await CleanupNestedScenarioAsync();
};

after = () => CleanupScenario();
}
### Class Level ###
<hr />

// ...
}
```
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 almost all sync test hooks and helpers you can find its corresponding async one:
<script src="https://gist.github.com/amirrajan/e53c12e92ba8b63f4b0bad60aaf118ff.js"></script>

| 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 |
For all sync test hooks at class level you can find its corresponding
async one, just by turning its signature to async:

Throughout the whole test class you can run both sync and async expectations as needed, so you can freely mix `it[]` and `itAsync[]`.
| 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()`

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:
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()`.

```c#
public class a_wrong_example_mixing_async_hooks_at_context_level : nspec
{
// Watch out, this example will not work
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()`.

void given_some_scenario()
{
// this one, together with ...
before = () => SetupScenario();
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):

// ... this other, will cause an error
beforeAsync = async () => await SetupScenarioAsync();
<script src="https://gist.github.com/amirrajan/36553f95d0aebf0036263b6d5c3cd4de.js"></script>

it["should do something sync"] = () => DoSomething();
### Context level ###
<hr />

itAsync["should do something async"] = async () => await DoSomethingAsync();
At a context and sub-context level, you need to set _asynchronous_
test hooks provided by NSpec, instead of the synchronous ones:

<script src="https://gist.github.com/amirrajan/5916225d1a536456ec0d5a49f28f4961.js"></script>

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:

<script src="https://gist.github.com/amirrajan/5916225d1a536456ec0d5a49f28f4961.js"></script>

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.

context["given some nested scenario"] = () =>
{
before = () => SetupNestedScenario();
Just look for `nspec`-derived classes in following files:

itAsync["is not yet implemented async"] = todoAsync;
* [Async Assert Tests](https://github.com/nspec/NSpec/tree/master/sln/test/NSpecSpecs/describe_RunningSpecs)
* [How Before/After Async is Implemented](https://github.com/nspec/NSpec/tree/master/sln/test/NSpecSpecs/describe_RunningSpecs/describe_before_and_after)

afterAsync = async () => await CleanupNestedScenarioAsync();
};
## Data-driven test cases ##
<hr />

after = () => CleanupScenario();
}
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:

// ...
}
```
1. Build a set of data points.
1. Name and assign an expectation for each data point by looping though the whole set.

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

* [NSpecSpecs/describe_RunningSpecs/describe_async_*](https://github.com/nspec/NSpec/tree/master/NSpecSpecs/describe_RunningSpecs)
* [NSpecSpecs/describe_RunningSpecs/describe_before_and_after/async_*](https://github.com/nspec/NSpec/tree/master/NSpecSpecs/describe_before_and_after)
* [NSpecSpecs/describe_RunningSpecs/Exceptions/when_async_*](https://github.com/nspec/NSpec/tree/master/NSpecSpecs/describe_RunningSpecs/Exceptions)

## Data-driven test cases

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:

1. build a set of data points;
1. name and assign an expectation for each data point by looping though the whole set.

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:

```c#
public class describe_prime_factors : nspec
{
void given_first_ten_integer_numbers()
{
new Each<int, int[]>
{
{ 0, new int[] { } },
{ 1, new int[] { } },
{ 2, new[] { 2 } },
{ 3, new[] { 3 } },
{ 4, new[] { 2, 2 } },
{ 5, new[] { 5 } },
{ 6, new[] { 2, 3 } },
{ 7, new[] { 7 } },
{ 8, new[] { 2, 2, 2 } },
{ 9, new[] { 3, 3 } },

}.Do((given, expected) =>
it["{0} should be {1}".With(given, expected)] = () => given.Primes().should_be(expected)
);
}
}
```
<script src="https://gist.github.com/amirrajan/2e32b75f13d5cb3c4d98beef2109bc37.js"></script>

## Additional info
<hr />

### Order of execution
<hr />

Please have a look at [this wiki page](https://github.com/nspec/NSpec/wiki/Execution-Orders) 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.
Please have a look
at
[this wiki page](https://github.com/nspec/NSpec/wiki/Execution-Orders)
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.

0 comments on commit b41e060

Please sign in to comment.