diff --git a/DesignTemplates/TestStackNuGetTemplate.svg b/DesignTemplates/TestStackNuGetTemplate.svg
new file mode 100644
index 0000000..59d5bae
--- /dev/null
+++ b/DesignTemplates/TestStackNuGetTemplate.svg
@@ -0,0 +1,182 @@
+
+
+
+
diff --git a/DesignTemplates/nugetlogo.png b/DesignTemplates/nugetlogo.png
new file mode 100644
index 0000000..3bb6424
Binary files /dev/null and b/DesignTemplates/nugetlogo.png differ
diff --git a/README.md b/README.md
index 7bfcfd1..5f4849c 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,8 @@ TestStack.docs
This repository is a [Pretzel](https://github.com/code52/pretzel) site. Pretzel is a .net implementation of [Jekyll](http://jekyllrb.com/docs/home/), but in a nutshell, the docs are made up from Markdown files.
-Contributions welcome, see [Contributing to TestStack](http://teststack.azurewebsites.net/Contributing.html) for more information
+Contributions welcome, see [Contributing to TestStack](http://docs.teststack.net/Contributing.html) for more information
To get started with Docs, just clone and run `RunDocs.cmd`, this will fire up a local web server and open the documentation site locally. Any changes you make will trigger a recompile of the documentation, so simply make changes, then refresh your browser to see them.
-Pull requests welcome!
+## Pull requests welcome!
diff --git a/_source/BDDfy/Customizing/ArchitectureOverview.md b/_source/BDDfy/Customizing/ArchitectureOverview.md
new file mode 100644
index 0000000..ad8db73
--- /dev/null
+++ b/_source/BDDfy/Customizing/ArchitectureOverview.md
@@ -0,0 +1,107 @@
+---
+layout: layout
+title: Architecture Overview
+order: 1
+---
+
+## Introduction ##
+This post provides an overview of the main components of the BDDfy architecture to provide some context for the rest of this section and to illustrate the extensibility points.
+
+
+
+The unit of operation in BDDfy is the Story. A Story has metadata (information about the Story) and a collection of Scenarios. Each Scenario represents a test class and contains metadata and a collection of Execution Steps, which are the methods on the test class. There are three types of architectural components in BDDfy: Scanners, Processors and Batch Processors. For each test class BDDfy composes a Story unit with various Scanners and passes it to the Processors in a processor pipeline. Once all of the test classes have been scanned and processed the Batch Processors run aggregate operations against all of the Stories.
+
+Scanners turn a call to BDDfy (from a method) into a Scenario which could potentially be related to a Story. BDDfy doesn't need Stories but if there is one it uses it. If a Scenario is not related to a Story then it is associated with a dummy placeholder. Each Story is then passed to the Processors, which perform various operations, including executing the tests, and populate the Stories, Scenarios and Steps with the test execution results. Once all of the tests have been scanned and processed, the Batch Processors take the collection of Stories and process their results. This could be any sort of aggregate operation, but currently all the batch processors are reports.
+
+## Scanners ##
+Most of the BDDfy in Action series so far has covered the various Scanners, so I won’t go into much detail here. Suffice to say, BDDfy uses Scanners to scan each test class to find all of the methods on it and turn the test class into a Scenario. The different Scanners are shown here:
+
+
+
+### Story Scanner ###
+BDDfy creates a Story Scanner for each test object. This is the Scanner that actually scans the test object and turns it into a Story. It composes together the Story Metadata Scanner and the appropriate Scenario Scanner – [Fluent](/BDDfy/Usage/FluentAPI.html) or [Reflective](/BDDfy/Usage/MethodNameConventions.html).
+
+The Story Metadata Scanner gets information from the Story attribute, if one exists on the class.
+
+A **Story** has the following properties:
+
+- **Story Metadata**: Information about the Story such as Title, As a, I want, So that
+- **Scenarios**: The collection of Scenarios related to the Story
+- **Result**: A Story’s Result is a Step Execution Result and is determined by the highest Step Execution Result of its Scenarios.
+- **Category**: The Story Category
+
+**Step Execution Results** have a numerical hierarchy and can be (in ascending order):
+
+- Not Executed (0)
+- Passed (1)
+- Not Implemented (2)
+- Inconclusive (3)
+- Failed (4)
+
+The Test Runner Processor assigns a numerical Step Execution Result to every Execution Step. The result of a Scenario is then determined by the highest value of from its Steps and the result of a Story is determined by the highest result of its Scenarios. For example, if a Step fails, then its parent Scenario and Story will also have a result of Failed.
+
+### Scenario Scanners ###
+Scenario Scanners scan the test class and use the information they find to create a Scenario. There is a Fluent Scenario Scanner and a Reflective Scenario Scanner.
+
+A **Scenario** has the following properties:
+
+- **Title**: The Scenario Title
+- **Steps**: The collection of Steps (test class methods) related to the Scenario
+- **Result**: A Scenario’s Result is a Step Execution Result and is determined by the highest Step Execution Result of its Steps.
+- **Duration**: How long the Scenario took to execute. Used by Diagnostics.
+
+### Step Scanners ###
+Step Scanners turn methods into Execution Steps. The Reflective Scanners (the Executable Attribute Step Scanner and the Method Name Scanner) scan the test class to find all the methods on it and turns them into Execution Steps. The Fluent Step Scanner is only a registry and in practice doesn't do any scanning.
+
+An **Execution Step** has the following properties:
+
+- **Title**: The Step Title
+- **Result**: The result of executing the Step.
+- **Duration**: How long the Step took to execute. Used by Diagnostics.
+- **Asserts**: Whether or not the Step is an Assertion Step.
+- **Should Report**: Whether the Step should be displayed in reports.
+- **Execution Order**: The order that the step should run in relative to the other steps. Can be (in ascending order)
+ - Initialize (for example, "Context", "Setup)
+ - Setup State ("Given")
+ - Consecutive Setup State ("And Given")
+ - Transition ("When)
+ - Consecutive Transition "And When")
+ - Assertion ("Then")
+ - Consecutive Assertion ("And Then")
+ - Tear Down ("TearDown")
+
+## Processors ##
+Once a test class has been scanned into a Story, the Story is passed into a **Processor pipeline** where a series of processing steps are performed on it. The Processors are categorized by Type and the order they run in is determined by this Type.
+
+The various **Process Types**, in order, are:
+
+1. Firstly
+1. Execute
+1. Before Report
+1. Report
+1. After Report
+1. Process Exceptions
+1. Finally
+
+
+
+
+1. **Test Runner (Execute)**: Executes the tests.
+1. **Console Reporter (Report)**: Displays the test result in the console.
+1. **Exception Processor (Process Exceptions)**: Handles exceptions.
+1. **Story Cache (Finally)**: Saves each Story for later processing by the Batch Processors.
+1. **Disposer (Finally)**: Cleans up the Story and its Scenarios.
+
+## Batch Processors ##
+Once all of the tests have been scanned and processed, the Batch Processors take all of the Stories and process their results (technically speaking they run in the [AppDomain DomainUnload event](http://msdn.microsoft.com/en-GB/library/system.appdomain.domainunload.aspx)). This could be any sort of result processing, but currently all the batch processors are reports. The built-in Batch Processors are displayed in the diagram below.
+
+
+
+- **HTML Reporter**: Creates the HTML report
+- **Markdown Reporter**: Creates the Markdown report
+- **Diagnostics Reporter**: Creates the Diagnostics report
+
+## Configurator ##
+The static Configurator class allows you to configure Scanners, Processors and Batch Processors. It lets you enable, disable, or replace individual components and it also allows you to add custom implementations.
+
+
diff --git a/_source/BDDfy/Customizing/CustomizingReports.md b/_source/BDDfy/Customizing/CustomizingReports.md
new file mode 100644
index 0000000..76db56d
--- /dev/null
+++ b/_source/BDDfy/Customizing/CustomizingReports.md
@@ -0,0 +1,181 @@
+---
+layout: layout
+title: Customizing Reports
+order: 2
+---
+
+This post looks at how to customize the HTML Report and how to create your own custom reports. You can customize the HTML report via configuration or by applying custom CSS or JavaScript. You can create custom reports either by implementing a new Processor or Batch Processor and adding them into their respective pipelines (see the [Architecture Overview](/BDDfy/Customizing/ArchitectureOverview.html) for details about the differences between these).
+
+## Customizing the HTML Report ##
+The HTML report is the most sophisticated report in [BDDfy](http://teststack.github.io/pages/BDDfy.html) and therefore provides a lot more things that you can configure. Its configuration is defined by the IHtmlReportConfiguration interface.
+
+ public interface IHtmlReportConfiguration
+ {
+ string ReportHeader { get; }
+ string ReportDescription { get; }
+ string OutputPath { get; }
+ string OutputFileName { get; }
+ bool RunsOn(Story story);
+ }
+
+You can create a new configuration by implementing that interface or you can inherit from the DefaultHtmlReportConfiguration class, used to configure the standard HTML Report, and just override specific properties. Here is an example of a custom configuration, taken from the ATM sample, available on github [here](https://github.com/TestStack/TestStack.BDDfy/tree/master/TestStack.BDDfy.Samples/Atm).
+
+ public class HtmlReportConfig : DefaultHtmlReportConfiguration
+ {
+ public override bool RunsOn(Core.Story story)
+ {
+ return story.MetaData.Type.Namespace != null && story.MetaData.Type.Namespace.EndsWith("Atm");
+ }
+
+ ///
+ /// Change the output file name
+ ///
+ public override string OutputFileName
+ {
+ get
+ {
+ return "ATM.html";
+ }
+ }
+
+ ///
+ /// Change the report header to your project
+ ///
+ public override string ReportHeader
+ {
+ get
+ {
+ return "ATM Solutions";
+ }
+ }
+
+ ///
+ /// Change the report description
+ ///
+ public override string ReportDescription
+ {
+ get
+ {
+ return "A reliable solution for your offline banking needs";
+ }
+ }
+ }
+
+which produces the following customised report, which you will find in your bin directory named ATM.html:
+
+
+
+
+The HTML report is a [Batch Processor](/BDDfy/Customizing/ArchitectureOverview.html) and is implemented by the HtmlReporter class. To plug the new report into BDDfy you need to create a new HtmlReporter and pass the custom configuration into its constructor. As I explained in the [Reports post](/BDDfy/Usage/Reports.html), the place to apply that configuration to BDDfy is the Configurator class, which is called before the tests run.
+
+ Configurator.BatchProcessors.Add(new HtmlReporter(new HtmlReportConfig()));
+
+The use of the Add method means this is adding a second HTML Report processor into the Batch Processor pipeline, so the default report runner will still run. If you actually want the new report to replace the default report, then you will also need to disable the default report.
+
+ Configurator.BatchProcessors.HtmlReport.Disable();
+
+## Custom CSS and JavaScript ##
+You can customize a lot more about the HTML report. BDDfy uses the BDDfy.css file to style the report and BDDfy.js and jQuery to add interactivity to it. You will find these files in the bin directory alongside the HTML report. You can customise the styles by adding a bddifyCustom.css class and the behaviour by adding a bddifyCustom.js file. These files also need to be in the same directory as the HTML report file. This will affect all the reports in the project.
+
+
+## Create a custom report by creating a new Processor ##
+One way to create a custom report is to implement a new Processor and plug it into the Processor pipeline. You just have to implement the one Process() method and set the Process Type to Report.
+
+An example of doing this is provided in the BDDfy Tic Tac Toe sample project with the Custom Text Reporter. The sample is available on [github](https://github.com/TestStack/TestStack.BDDfy/tree/master/TestStack.BDDfy.Samples) or [nuget](http://nuget.org/packages/TestStack.BDDfy.Samples/).
+
+ ///
+ /// This is a custom reporter that shows you how easily you can create a custom report.
+ /// Just implement IProcessor and you are done
+ ///
+ public class CustomTextReporter : IProcessor
+ {
+ private static readonly string Path;
+
+ private static string OutputDirectory
+ {
+ get
+ {
+ string codeBase = typeof(CustomTextReporter).Assembly.CodeBase;
+ var uri = new UriBuilder(codeBase);
+ string path = Uri.UnescapeDataString(uri.Path);
+ return System.IO.Path.GetDirectoryName(path);
+ }
+ }
+
+ static CustomTextReporter()
+ {
+ Path = System.IO.Path.Combine(OutputDirectory, "BDDfy-text-report.txt");
+
+ if(File.Exists(Path))
+ File.Delete(Path);
+
+ var header =
+ " A custom report created from your test assembly with no required configuration " +
+ Environment.NewLine +
+ Environment.NewLine +
+ Environment.NewLine +
+ Environment.NewLine;
+ File.AppendAllText(Path, header);
+ }
+
+ public void Process(Story story)
+ {
+ // use this report only for tic tac toe stories
+ if (!story.MetaData.Type.Name.Contains("TicTacToe"))
+ return;
+
+ var scenario = story.Scenarios.First();
+ var scenarioReport = new StringBuilder();
+ scenarioReport.AppendLine(string.Format(" SCENARIO: {0} ", scenario.Title));
+
+ if (scenario.Result != StepExecutionResult.Passed && scenario.Steps.Any(s => s.Exception != null))
+ {
+ scenarioReport.Append(string.Format(" {0} : ", scenario.Result));
+ scenarioReport.AppendLine(scenario.Steps.First(s => s.Result == scenario.Result).Exception.Message);
+ }
+
+ scenarioReport.AppendLine();
+
+ foreach (var step in scenario.Steps)
+ scenarioReport.AppendLine(string.Format(" [{1}] {0}", step.StepTitle, step.Result));
+
+ scenarioReport.AppendLine("--------------------------------------------------------------------------------");
+ scenarioReport.AppendLine();
+
+ File.AppendAllText(Path, scenarioReport.ToString());
+ }
+
+ public ProcessType ProcessType
+ {
+ get { return ProcessType.Report; }
+ }
+ }
+
+This produces the BDDfy-text-report.txt text file report which is output to the bin directory:
+
+
+
+## Create a custom report by creating a new Batch Processor ##
+While that is one way that you can create a custom report, probably the better way to do it is to implement a new Batch Processor. The Processor runs as each test is being executed and allows you to build up the report, whereas a Batch Processor has the advantage of running after all of the tests have finished, meaning that you have access to total and summary information, such as diagnostics.
+
+As an example of creating a custom report by creating a new Batch Processor, I was recently messing around with running tests in parallel and, as you might expect, the normal console report was quite jumbled. Multiple console report Processors were writing to the console at the same time and different test results were overlapping. The solution was to run the console report after all of the tests had run by creating a new Console Reporter as a Batch Processor rather than a Processor.
+
+ public class MyConsoleReporter : IBatchProcessor
+ {
+ public void Process(IEnumerable stories)
+ {
+ var reporter = new ConsoleReporter();
+ stories
+ .ToList()
+ .ForEach(story => reporter.Process(story));
+ }
+ }
+
+Then I just needed to add it to the Batch Processor pipeline and disable the built-in console report:
+
+ Configurator.Processors.ConsoleReport.Disable();
+ Configurator.BatchProcessors.Add(new MyConsoleReporter());
+
+
+That's a bit of a hack for demo purposes. The HTML, MarkDown, and Diagnostics reports are all implemented as Batch Processors and I would recommend checking them out for examples of how to create a new report.
+
diff --git a/_source/BDDfy/Customizing/ExampleCustomFramework.md b/_source/BDDfy/Customizing/ExampleCustomFramework.md
new file mode 100644
index 0000000..a6a72a1
--- /dev/null
+++ b/_source/BDDfy/Customizing/ExampleCustomFramework.md
@@ -0,0 +1,397 @@
+---
+layout: layout
+title: Example - Creating a custom framework
+order: 3
+---
+
+## Introduction ##
+This post provides some sample code to demonstrate some of the BDDfy extension points, and to provide a reference point for some of your own customizations. In this example we modify BDDfy to work as a context specification framework, similar to [mspec](https://github.com/machine/machine.specifications).
+
+So, the requirements for this hypothetical custom framework are
+
+- It should use the context specification grammar (Establish Context, Because Of, It Should) rather than the Given When Then grammar that BDDfy uses by default
+- It should have a Class per Scenario style, like the [Testcase class per Fixture](http://xunitpatterns.com/Testcase%20Class%20per%20Fixture.html) pattern from Gerard Meszaros’ [XUnit Test Patterns](http://www.amazon.com/xUnit-Test-Patterns-Refactoring-Code/dp/0131495054/ref=sr_1_1?s=books&ie=UTF8&qid=1368296304&sr=1-1&keywords=xunit+test+patterns) book (a book which I highly recommend).
+- It should not require any testing framework, such as NUnit or MsTest. In fact, the only dependency will be BDDfy.
+- It should not require a test runner, such as TestDriven.Net or ReSharper
+- I’m not a fan of attributes, so no attributes for the BDDfy Story, the test fixture or for the test methods
+- The HTML report should be customised for my new framework. (Let’s call it Context Specifier).
+- Finally, I would like to be able to run tests sequentially or in parallel
+
+## ContextSpecification Base Class ##
+
+I am going to start off with a base class to establish the context specification method template. I’ll also put a Story property here as an alternative to the Story attribute so that each specification can specify the Story that they belong to. BDDfy does not require Scenarios to have a Story, but it’s a convention I’m wanting to enforce for this framework! The Run method is what calls BDDfy to run the test. By default it will call BDDfy() and BDDfy will convert the class name into the scenario title. If I want to override that value I just need to set the ScenarioTitle in the class constructor and then it will be used with the BDDfy(scenarioTitle) overload.
+
+This is actually the only time that BDDfy is called in this framework and the whole test suite. It is very DRY and I much prefer it to adding fixture and test attributes for every test. (By the way, even if I were using a framework like nunit/xunit this base class would still be the only place I would need to use their attributes or call BDDfy. Their runners are smart enough to find the test classes, which is a nice benefit of using inheritance in this sort of framework).
+
+ public abstract class ContextSpecification
+ {
+ protected virtual void EstablishContext() { }
+ protected virtual void BecauseOf() { }
+
+ public abstract UserStory Story { get; }
+
+ public virtual string ScenarioTitle { get; set; }
+
+ public void Run()
+ {
+ if (string.IsNullOrEmpty(ScenarioTitle))
+ {
+ this.BDDfy();
+ }
+ else
+ {
+ this.BDDfy(ScenarioTitle);
+ }
+ }
+ }
+
+With this base class in place I can write a class for each scenario. To do that, I’m going to modify the BDDfy ATM samples. The Context Specification style utilises the same four-phase test pattern as Arrange Act Assert or Given When Then, setting up the pre-conditions for the test in the Establish Context phase, exercising the system under test (SUT) in the Because Of phase, and then verifying expectations in the It Should phase.
+
+The user story is associated with the specification by setting the Story property to a new instance of a UserStory class - in this case the AtmWithdrawCashStory class. Notice that I am able to override the scenario title in the constructor, so the report will read "When account funds are less than zero" rather than "When account has insufficient funds" which BDDfy would derive from the class name.
+
+ public class WhenAccountHasInsufficientFunds : ContextSpecification
+ {
+ private Card _card;
+ private Atm _atm;
+
+ public WhenAccountHasInsufficientFunds()
+ {
+ ScenarioTitle = "When account funds are less than zero";
+ }
+
+ protected override void EstablishContext()
+ {
+ _card = new Card(true, 10);
+ _atm = new Atm(100);
+ }
+
+ protected override void BecauseOf()
+ {
+ _atm.RequestMoney(_card, 20);
+ }
+
+ public override UserStory Story
+ {
+ get { return new AtmWithdrawCashStory(); }
+ }
+
+ void ItShouldNotDispenseAnyMoney()
+ {
+ Assert.AreEqual(0, _atm.DispenseValue);
+ }
+
+ void AndItShouldSayThereAreInsufficientFunds()
+ {
+ Assert.AreEqual(DisplayMessage.InsufficientFunds, _atm.Message);
+ }
+
+ void AndItShouldHaveTheSameAccountBalance()
+ {
+ Assert.AreEqual(10, _card.AccountBalance);
+ }
+
+ void AndItShouldReturnTheCard()
+ {
+ Assert.IsFalse(_atm.CardIsRetained);
+ }
+ }
+
+## The Context Specification Grammar ##
+
+To use my new grammar instead of the default BDDfy Given When Then grammar I need to tell BDDfy how to find step methods on the class, which I can do by replacing the BDDfy DefaultMethodNameStepScanner with a new MethodNameStepScanner. BDDfy defines the Given When Then grammar in the DefaultMethodNameStepScanner and the easiest way to create a new scanner is to copy and modify that.
+
+ public class ContextSpecificationStepScanner : MethodNameStepScanner
+ {
+ public ContextSpecificationStepScanner()
+ : base(
+ CleanupTheStepText,
+ new[]
+ {
+ new MethodNameMatcher(s => s.StartsWith("EstablishContext", StringComparison.OrdinalIgnoreCase), false, ExecutionOrder.SetupState, false),
+ new MethodNameMatcher(s => s.StartsWith("BecauseOf", StringComparison.OrdinalIgnoreCase), false, ExecutionOrder.Transition, false),
+ new MethodNameMatcher(s => s.StartsWith("It", StringComparison.OrdinalIgnoreCase), true, ExecutionOrder.Assertion, true),
+ new MethodNameMatcher(s => s.StartsWith("AndIt", StringComparison.OrdinalIgnoreCase), true, ExecutionOrder.ConsecutiveAssertion, true)
+ })
+ {
+ }
+
+ static string CleanupTheStepText(string stepText)
+ {
+ if (stepText.StartsWith("EstablishContext", StringComparison.OrdinalIgnoreCase))
+ return "Establish context ";
+
+ if (stepText.StartsWith("BecauseOf", StringComparison.OrdinalIgnoreCase))
+ return "Because of ";
+
+ if (stepText.StartsWith("AndIt ", StringComparison.OrdinalIgnoreCase))
+ return stepText.Remove("and".Length, "It".Length);
+
+ return stepText;
+ }
+ }
+
+The custom scanner requires two parameters in the constructor; a step text transformer and an array of MethodNameMatchers. Each MethodNameMatcher defines a predicate to identify if the method matches, whether or not the method is an assertion, the type of method it is, and whether it should be displayed on the report. With Given When Then steps you want to display all of the steps on the report. With my context specification grammar I just want to display the name of the class and the assertions, so I specify false for the shouldReport parameter of the “EstablishContext” and “BecauseOf” steps so that they won’t display on the report. This is not something we recommend, as it would be helpful to receive error messages for those steps if they fail, but it makes sense for reporting purposes with this grammar as repeating EstablishContext and BecauseOf would not be much use on the report (and a reason why Given When Then grammar is preferable to this one).
+
+Once I have my new grammar I need to tell BDDfy to use it by using the Configurator to disable the default scanner and to add the new one:
+
+ Configurator.Scanners.DefaultMethodNameStepScanner.Disable();
+ Configurator.Scanners.Add(() => new ContextSpecificationStepScanner());
+
+## Replacing the BDDfy Story Attribute ##
+
+The next thing on my list is the Story. BDDfy uses an attribute on the test class to specify the Story that the Scenario test class belongs to. You are not limited to this though, and like most things in BDDfy, you are able to customise this behaviour if you want. I’m going to create a simple UserStory class that stores Story metadata. It is just a standard class that does not implement any BDDfy behaviours. I could potentially just use the BDDfy StoryMetaData class directly, but I will keep things separate for now.
+
+ public abstract class UserStory
+ {
+ public string Title { get; set; }
+ public string AsA { get; set; }
+ public string IWant { get; set; }
+ public string SoThat { get; set; }
+ }
+
+
+To create a new story class, I just have to inherit from the UserStory class and set the metadata properties in its constructor. Here is the AtmWithdrawCashStory from the example above.
+
+ public class AtmWithdrawCashStory : UserStory
+ {
+ public AtmWithdrawCashStory()
+ {
+ Title = "Withdrawing cash from the ATM";
+ AsA = "As an Account Holder";
+ IWant = "I want to withdraw cash from an ATM";
+ SoThat = "So that I can get money when the bank is closed";
+ }
+ }
+
+To tell BDDfy how to find these Stories and match them to the appropriate Scenario, I need to implement a new Story Metadata Scanner to associate the Scenario test class with its Story.
+
+ public class StoryMetaDataScanner : IStoryMetaDataScanner
+ {
+ public StoryMetaData Scan(object testObject, Type explicitStoryType = null)
+ {
+ var specification = testObject as ContextSpecification;
+ if (specification == null)
+ return null;
+
+ var story = specification.Story;
+
+ return new StoryMetaData(story.GetType(), story.AsA, story.IWant, story.SoThat, story.Title);
+ }
+ }
+
+My convention of having all my test classes inherit from the base ContextSpecifcation comes in handy here as it enables me to easily get access to the Story property and use it to create the StoryMetaData that BDDfy needs. Now, I can just replace the BDDfy StoryMetaDataScanner with my custom one using the Configurator.
+
+ Configurator.Scanners.StoryMetaDataScanner = () => new StoryMetaDataScanner();
+
+## Customizing the HTML Report ##
+
+I want to change the HTML report to have the name and description of the framework and to change the name of the html file that is generated. You can do this by inheriting from the DefaultHtmlReportConfiguration class.
+
+ public class CustomHtmlReportConfiguration : DefaultHtmlReportConfiguration
+ {
+ public override string ReportHeader
+ {
+ get
+ {
+ return "Context Specifier";
+ }
+ }
+
+ public override string ReportDescription
+ {
+ get
+ {
+ return "A simple context specification framework for .Net developers";
+ }
+ }
+
+ public override string OutputFileName
+ {
+ get
+ {
+ return "ContextSpecifications.html";
+ }
+ }
+ }
+
+I can then turn off the default HTML Report and plug the custom one in instead:
+
+ Configurator.BatchProcessors.HtmlReport.Disable();
+ Configurator.BatchProcessors.Add(new HtmlReporter(new CustomHtmlReportConfiguration()));
+
+## Test Runner ##
+The Test Runner is quite straightforward. We just have to instantiate all of the specification classes and call the Run method. You could new up each class, use reflection to find all the classes that derive from ContextSpecification (as I’ve done here) or use an IoC container to store and retrieve them.
+
+ public class TestRunner
+ {
+ public void Run()
+ {
+ RunTestsSequentially();
+ RunBatchProcessors();
+ }
+
+ private void RunTestsSequentially()
+ {
+ //new WhenAccountHasInsufficientFunds().Run();
+ //new WhenCardHasBeenDisabled().BDDfy();
+ //new WhenAccountHasSufficientFunds().BDDfy();
+
+ GetSpecs().Each(spec => SafeRunSpec(spec));
+ }
+
+ private void SafeRunSpec(ContextSpecification spec)
+ {
+ try
+ {
+ spec.Run();
+ }
+ catch (Exception)
+ {
+ }
+ }
+
+ private void RunBatchProcessors()
+ {
+ foreach (var batchProcessor in Configurator.BatchProcessors.GetProcessors())
+ {
+ batchProcessor.Process(StoryCache.Stories);
+ }
+ }
+
+ private List GetSpecs()
+ {
+ return this.GetType()
+ .Assembly
+ .GetTypes()
+ .Where(type => type.IsSubclassOf(typeof(ContextSpecification)))
+ .Select(Activator.CreateInstance)
+ .Cast()
+ .ToList();
+ }
+ }
+
+
+If you were to just run the tests like this then you would see the Console Report display all the tests in the console window. However, none of the Batch Processors, such as the HTML Report, would run. BDDfy runs these in the AppDomain_Unload event and one down side of my running in a console app is that [this event is not raised in the default application domain](http://msdn.microsoft.com/en-us/library/system.appdomain.domainunload%28VS.90%29.aspx). No worries, BDDfy makes it easy to run ourselves so I’ve added the RunBatchProcessors method. If you check the bin directory of the console application you will see our customised HTML Report with the context specification grammar.
+
+## Wiring It All Up ##
+All that is left is to wire up a console application to run the tests. The first step is to configure BDDfy, which I’ve already shown. To run the tests I just need to instantiate the TestRunner and call the Run method.
+
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ ConfigureBDDfy();
+ new TestRunner().Run();
+
+ Console.ReadLine();
+ }
+
+ private static void ConfigureBDDfy()
+ {
+ Configurator.Scanners.DefaultMethodNameStepScanner.Disable();
+ Configurator.Scanners.Add(() => new ContextSpecificationStepScanner());
+
+ Configurator.Scanners.StoryMetaDataScanner = () => new StoryMetaDataScanner();
+
+ Configurator.BatchProcessors.HtmlReport.Disable();
+ Configurator.BatchProcessors.Add(new HtmlReporter(new CustomHtmlReportConfiguration()));
+ }
+ }
+
+If you go to the bin directory of the test project you should see the report file with the custom name, "ContextSpecifications.html," and all of the custom content inside it.
+
+
+
+
+# Running the tests in parallel #
+In the never ending quest for faster running tests, being able to run them in parallel can be a great way to speed things up. The first problem I have to solve is to batch up the list of tests into smaller lists of a fixed size that can be run in parallel. I found an excellent extension method for that by David Boike [here](http://www.make-awesome.com/2010/08/batch-or-partition-a-collection-with-linq/):
+
+ public static class Extensions
+ {
+ public static IEnumerable> Batch(this IEnumerable collection, int batchSize)
+ {
+ List nextbatch = new List(batchSize);
+ foreach (T item in collection)
+ {
+ nextbatch.Add(item);
+ if (nextbatch.Count == batchSize)
+ {
+ yield return nextbatch;
+ nextbatch = new List(batchSize);
+ }
+ }
+ if (nextbatch.Count > 0)
+ yield return nextbatch;
+ }
+ }
+
+Then I can add a method to the TestRunner that uses the Batch extension method to break the list of tests into batches that can be run using the [Parallel ForEach](http://msdn.microsoft.com/en-us/library/system.threading.tasks.parallel.foreach.aspx) method. This is the parallel version of the standard, sequential foreach loop.
+
+ private void RunTestsInParallel(int batchSize)
+ {
+ List theSpecs = GetSpecs();
+ var batch = theSpecs.Batch(batchSize);
+
+ Parallel.ForEach(batch, specs => specs.Each(spec => SafeRunSpec(spec)));
+ }
+
+
+This can be plugged into the Run method by adding an optional batch size parameter, which allows the existing code to work as it is and for tests to run in parallel by passing a batchSize value of more than zero into the Run method.
+
+ public class TestRunner
+ {
+ public void Run(int batchSize = 0)
+ {
+ if (batchSize == 0)
+ {
+ RunTestsSequentially();
+ }
+ else
+ {
+ RunTestsInParallel(batchSize);
+ }
+ RunBatchProcessors();
+ }
+ ...
+ }
+
+## Batch Console Reporter ##
+There is one problem with this code though. The parallel nature of the loop means that multiple iterations may be executing at the same time and, as might be expected, the normal console report becomes quite jumbled.
+
+
+
+The solution is to run the console report after all of the tests have completed. This can be achieved by creating a new Console Reporter as a Batch Processor rather than a Processor. The Processor runs as each test is being executed and allows you to build up the report, whereas a Batch Processor has the advantage of running after all of the tests have finished (see the [Architecture Overview](/BDDfy/Customizing/ArchitectureOverview.html) post for more detail).
+
+ public class MyConsoleReporter : IBatchProcessor
+ {
+ public void Process(IEnumerable stories)
+ {
+ var reporter = new ConsoleReporter();
+ stories
+ .ToList()
+ .ForEach(story => reporter.Process(story));
+ }
+ }
+
+Then I just needed to add it to the Batch Processor pipeline and disable the built-in console report. For convenience I have just added it to the RunTestsInParallel method:
+
+ private void RunTestsInParallel(int batchSize)
+ {
+ Configurator.Processors.ConsoleReport.Disable();
+ Configurator.BatchProcessors.Add(new BatchConsoleReporter());
+
+ List theSpecs = GetSpecs();
+ var batch = theSpecs.Batch(batchSize);
+
+ Parallel.ForEach(batch, specs => specs.Each(spec => SafeRunSpec(spec)));
+ }
+
+
+
+
+So, there it is. A bit of a contrived example, but hopefully it has highlighted some of the extensibility points that BDDfy offers and provided some food for thought for some customizations that you might like to try yourself.
+
+The code is available on github:
+[https://github.com/mwhelan/BDDfySamples](https://github.com/mwhelan/BDDfySamples)
+
+
diff --git a/_source/BDDfy/Customizing/ExampleUnitTesting.md b/_source/BDDfy/Customizing/ExampleUnitTesting.md
new file mode 100644
index 0000000..9ce6da0
--- /dev/null
+++ b/_source/BDDfy/Customizing/ExampleUnitTesting.md
@@ -0,0 +1,235 @@
+---
+layout: layout
+title: Example - Using BDDfy for unit testing
+order: 3
+---
+
+# Introduction #
+I’ve been using BDDfy with NUnit for writing acceptance tests for quite awhile now. But for unit testing I have continued to use mspec with [machine fakes](https://github.com/machine/machine.fakes) and Moq for auto-mocking. The more I used BDDfy, the more I liked it, and the less I liked the context switch into another framework. I found myself wanting to write unit tests in the Given When Then format and didn't like having to maintain two sets of helper code for NUnit and mspec. I loved the reporting in BDDfy and started to think it would be pretty cool to have similar reporting for my unit tests. Basically, I wanted a consistent experience across all my automated testing.
+
+MSpec uses the [testcase class per fixture](http://xunitpatterns.com/Testcase%20Class%20per%20Fixture.html) style of testing, which is how I use BDDfy for acceptance testing, so it makes sense to continue with that style for the BDDfy unit tests. When I am doing acceptance tests I have a base ScenarioFor< T> class, where the T represents the System Under Test (SUT). Because these are full system tests, I resolve this SUT using the same inversion of control container that my application uses, which works nicely. The IoC container acts as a [SUT factory](http://blog.ploeh.dk/2009/02/13/SUTFactory/). With unit tests, I also want to have a SUT factory, but instead I want it to be an auto-mocking container.
+
+When we started writing unit tests for the [Seleno](http://teststack.github.io/pages/Seleno.html) project, I thought it would be a good opportunity to try some of these ideas out.
+
+# Specification Base Fixture #
+The Specification base fixture class wires up BDDFy. It provides methods that BDDfy knows about in its default configuration for setting up and tearing down the fixture (each test class will implement its own specific Given When Then methods that BDDfy will also find). The Run method has the NUnit Test attribute and so will be called by the testing framework and it just calls BDDFy to run the test. I prefer to use NUnit myself, but you could just as easily substitute XUnit or MsTest attributes if you prefer. One thing I really like about this approach is that I only have to put the TestFixture and Test attributes in this one class and then all of the test classes I create inherit them and don't need any attributes. All the test runners still picks the classes up as tests and ReSharper even puts its little run test icons in each test class as normal. This class is also the one place that BDDfy gets called.
+
+ [TestFixture]
+ public abstract class Specification : ISpecification
+ {
+ [Test]
+ public virtual void Run()
+ {
+ string title = BuildTitle();
+ this.BDDfy(title, Category);
+ }
+
+ protected virtual string BuildTitle()
+ {
+ return Title ?? GetType().Name.Humanize(LetterCasing.Title);
+ }
+
+ // BDDfy methods
+ public virtual void EstablishContext() { }
+ public virtual void Setup() { }
+ public virtual void TearDown() { }
+
+ public virtual Type Story { get { return GetType(); } }
+ public virtual string Title { get; set; }
+ public string Category { get; set; }
+ }
+
+#Auto-Mocking #
+The SpecificationFor class inherits from the Specification class and adds an auto-mocking container for creating the SUT. An auto-mocking container decouples a unit test from the mechanics of creating the SUT and automatically supplies dynamic mocks in place of all of the SUT's dependencies. They are commonly implemented by combining an IoC container with a mocking framework, which is what I will be doing here. I prefer [NSubstitute](http://nsubstitute.github.io/) for mocking these days, so my friend [Rob Moore's](http://robdmoore.id.au/) [AutoSubstitute](http://nuget.org/packages/AutofacContrib.NSubstitute) auto-mocking container is ideal.
+
+The AutoSubstitute field provides access to the container for full access to its functionality. SubstituteFor is provided as a convenience method to gain access to NSubstitute substitutes.
+
+ public abstract class SpecificationFor : Specification
+ {
+ public T SUT { get; set; }
+ protected AutoSubstitute AutoSubstitute;
+
+ protected SpecificationFor()
+ {
+ AutoSubstitute = CreateContainer();
+ InitialiseSystemUnderTest();
+ }
+
+ public virtual void InitialiseSystemUnderTest()
+ {
+ SUT = AutoSubstitute.Resolve();
+ }
+
+ public TSubstitute SubstituteFor() where TSubstitute : class
+ {
+ return AutoSubstitute.ResolveAndSubstituteFor();
+ }
+
+ public override Type Story
+ {
+ get { return typeof(T); }
+ }
+
+ private static AutoSubstitute CreateContainer()
+ {
+ Action autofacCustomisation = c => c
+ .RegisterType()
+ .FindConstructorsWith(t => t.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
+ .PropertiesAutowired();
+ return new AutoSubstitute(autofacCustomisation);
+ }
+ }
+
+Notice that the InitialiseSystemUnderTest is virtual, so if you need to create the SUT yourself rather than having AutoSubstitute do it then you can just override it in your test class. It runs before any of the test methods so they can all safely interact with the SUT confident that it has been created.
+
+# What's in a Story? #
+When moving from scenarios to unit tests I had to think what I wanted to do with the Story that is part of BDD. BDDfy actually doesn't require stories, so one option was to do nothing. However, when you don't have a story the report shows the namespace instead, and I don't find that particularly helpful or attractive. A Story's purpose in BDD is to group related Scenarios and to provide metadata about that grouping, so I think the same logic can be applied to unit tests.
+
+By creating a base specification class that related tests inherit from, they will all be grouped together on the report, and the name of that class will be used in the heading, much like a Story grouping would look like on a BDD report. This class is often a convenient place to create variables that are common to all of the test cases. So, for example, if you create a base class called PageNavigatorSpecification, the report grouping will read "Specifications For: Page Navigator." That is achieved by adding the following custom story metadata scanner.
+
+ public class SpecStoryMetaDataScanner : IStoryMetaDataScanner
+ {
+ public virtual StoryMetaData Scan(object testObject, Type explicityStoryType = null)
+ {
+ var specification = testObject as ISpecification;
+ if (specification == null)
+ return null;
+
+ string specificationTitle = CreateSpecificationTitle(specification);
+ var story = new StoryAttribute() {Title = specificationTitle};
+ return new StoryMetaData(specification.Story, story);
+ }
+
+ private string CreateSpecificationTitle(ISpecification specification)
+ {
+ string suffix = "Specification";
+ string title = specification.Story.Name;
+ if (title.EndsWith(suffix))
+ title = title.Remove(title.Length - suffix.Length, suffix.Length);
+ return title;
+ }
+ }
+
+And here is what the specifications look like in the BDDfy report:
+
+
+
+# Unit Testing #
+Here is an example of some tests that we have written for Seleno using this approach. Firstly, an example of a Specification class, the grouping class analagous to the BDDFy story that all the PageReader specification classes will inherit from. There is normally no need to override the Story as the SpecificationFor class will automatically convert the generic T into an English name. In this case though it will convert PageReader< TestViewModel> into PageReader`, so it is a convenient hack to override the property with PageReaderSpecification which will more attractively produce "Page Reader" on the report.
+
+ abstract class PageReaderSpecification : SpecificationFor>
+ {
+ public override Type Story
+ {
+ get { return typeof (PageReaderSpecification); }
+ }
+ }
+
+Each specification for the PageReader component inherits from the PageReaderSpecification. There are often no Given steps as the auto-mocking container has taken care of instantiating the SUT for you. It has also created a Substitute for the IExecutor dependency of the PageReader class and in the verification phase of the test you can just call SubstituteFor< IExecutor> to call NSubstitute verification methods on the substitute.
+
+ class When_checking_an_element_exists_and_is_visible_with_property : PageReaderSpecification
+ {
+ public When_checking_an_element_exists_and_is_visible_with_property()
+ {
+ SUT.ExistsAndIsVisible(x => x.Item);
+ }
+
+ public void Then_it_should_execute_the_relevant_script_with_jquery_id_selector()
+ {
+ SubstituteFor()
+ .Received()
+ .ScriptAndReturn("$(\"#Item\").is(':visible')");
+ }
+ }
+
+This test shows that you can also setup Substitute behaviour in the setup phase of the test.
+
+ class When_getting_a_web_element_strongly_typed_text : PageReaderSpecification
+ {
+ private DateTime _result;
+ private readonly DateTime _the03rdOfJanuary2012At21h21 = new DateTime(2012, 01, 03, 21, 21, 00);
+
+ [Given("Given a web element contains the text 03/01/2012 21:21")]
+ public void Given_a_web_element_contains_the_text_03_01_2012_21_21()
+ {
+ SubstituteFor()
+ .Element(Arg.Any())
+ .Returns(SubstituteFor());
+
+ SubstituteFor().Text.Returns("03/01/2012 21:21");
+ }
+
+ public void When_getting_the_web_element_matching_a_view_model_property()
+ {
+ _result = SUT.TextAsType(viewModel => viewModel.Modified);
+ }
+
+ public void Then_it_should_return_the_corresponding_typed_value_of_the_web_element_text()
+ {
+ _result.Should().Be(_the03rdOfJanuary2012At21h21);
+ }
+ }
+
+# Another approach #
+It's worth looking at another example of sharing a base context class. As I said above, it is not just for making the report work, it can be quite helpful to share context there. [Matt Honeycutt](http://trycatchfail.com/blog/) has an interesting style for reusing context classes in his very cool [SpecsFor BDD framework](http://specsfor.com/) that this approach also supports:
+
+ public class given
+ {
+ public abstract class the_command_is_valid : SpecificationFor
+ {
+ protected void Given_the_command_is_valid()
+ {
+ SubFor>().Validate(Arg.Any()).Returns(new ExecutionResult(null));
+ SubFor().ValidatorForCommand(Arg.Any()).Returns(SubFor>());
+ }
+ }
+ }
+
+ public class processing_a__valid_command : given.the_command_is_valid
+ {
+ private TestCommand _command = new TestCommand();
+ private ExecutionResult _result;
+
+ public void when_processing_a_valid_command()
+ {
+ _result = SUT.Execute(_command);
+ }
+
+ public void Then_the_processor_should_find_the_validator_for_the_command()
+ {
+ SubFor().Received().ValidatorForCommand(_command);
+ }
+
+ public void AndThen_validate_the_command()
+ {
+ SubFor>().Received().Validate(_command);
+ }
+
+ public void AndThen_the_processor_should_find_the_handler_for_the_command()
+ {
+ SubFor().Received().HandlerForCommand(_command);
+ }
+
+ public void AndThen_the_command_is_processed_successfully()
+ {
+ _result.IsSuccessful.Should().BeTrue();
+ }
+
+ public void AndThen_the_result_is_logged()
+ {
+ SubFor().Received().Info(Arg.Any());
+ }
+ }
+
+And this comes out very nicely on the report:
+
+
+
+
+You can find the code on [github](https://github.com/TestStack/TestStack.Seleno/tree/master/src/TestStack.Seleno.Tests/Specify).
+
+
+
+
+
+
diff --git a/_source/BDDfy/Customizing/index.md b/_source/BDDfy/Customizing/index.md
new file mode 100644
index 0000000..ba72eb4
--- /dev/null
+++ b/_source/BDDfy/Customizing/index.md
@@ -0,0 +1,9 @@
+---
+layout: layout
+title: Customizing BDDfy
+order: 2
+---
+
+BDDfy strives to be very extensible: Its core barely has any logic in it all its responsibilities are delegated to extensions, all of which are configurable. For example, if you don't like the reports it generates, you can write your custom reporter in a few lines of code.
+
+This section will look at the extensibility points and provide samples of customizing BDDfy.
diff --git a/_source/BDDfy/Usage/MethodNameConventions.md b/_source/BDDfy/Usage/MethodNameConventions.md
index 2bc56ba..ca9839b 100644
--- a/_source/BDDfy/Usage/MethodNameConventions.md
+++ b/_source/BDDfy/Usage/MethodNameConventions.md
@@ -6,14 +6,16 @@ order: 1
BDDfy can scan your tests in one of two ways: using Reflective API and Fluent API. Reflective API uses some hints to scan your classes and afterwards pretty much all the burden is on BDDfy's shoulders to find your steps, make sense of them and execute them in order. You can provide these hints in two ways: using method name conventions and/or attributes. For this post we will only concentrate on method name conventions.
-BDDfy uses quite a bit of magic to figure out what your scenario looks like and what it should execute. Despite the amount of magic gone into implementing the logic the programmers' API is extremely simple and it basically boils down to 14 letters:
+BDDfy uses a bit of magic to figure out what your scenario looks like and what it should execute. Despite the magic behind the scenes, using the BDDfy API is extremely simple - it boils down to 14 letters:
this.BDDfy();
-That is all the API you need to know to be able to use BDDfy in Reflective Mode. Well, that and a bit of knowledge about the conventions which we are going to discuss in this post.
+That is all the API you need to know to be able to use BDDfy in Reflective Mode. Well, that and a bit of knowledge about the conventions described below.
##A class per scenario
-In the reflective mode BDDfy associates each class with a scenario and you will basically end up with one class per scenario. Some developers like the Single Responsibility Principle forced nature of this approach and some do not quite like it. For those who think this is not very DRY (Don't Repeat Yourself) BDDfy allows you to take full control over this using the Fluent API. I personally use both approaches in every project because each has its pros and cons.
+In the reflective mode BDDfy associates each class with a scenario and you will basically end up with one class per scenario.
+
+Some developers like the Single Responsibility Principle forced nature of this approach and some do not. For those who think this is not very DRY (Don't Repeat Yourself) BDDfy allows you to take full control over this using the Fluent API. I personally use both approaches in every project because each has its pros and cons.
A typical example of using method name convention looks like:
@@ -56,7 +58,7 @@ Using that one line of code BDDfy was able to find out what your scenario title

##How does BDDfy do all that?
-When using the reflective mode, BDDfy scans your class (which is this you are calling BDDfy() on) and finds all the methods in it. It then adds all the methods which match its conventions to a list. After having gone through the class (and its base classes) it loops over the methods and runs them and then generates a report from it.
+When using the reflective mode, BDDfy scans your class (which is this you are calling BDDfy() on) and finds all the methods in it. It then adds all the methods which match its conventions to a list. After having gone through the class (and its base classes), it loops over the methods, executes them, and then generates a report.
Here is the complete list of the out of the box conventions. The method name:
@@ -70,11 +72,11 @@ Here is the complete list of the out of the box conventions. The method name:
* starting with "And" is considered as an asserting method (reported).
* starting with "TearDown" is considered as a finally method which is run after all the other steps (not reported).
-Some of these conventions lead to the step not being reported and some report the step. For example if your method name ends with the word 'Context' the step will be picked up by the framework and will be executed; but it will not be reported in console or html report. This was created on a request by a user; but I personally do not use this feature. If I need to setup my state I either do it in the 'Given' steps or in the class constructor if it is not directly related to the scenario state.
+Some of these special conventions will lead to the step not being reported. For example if your method name ends with the word 'Context' the step will be picked up by the framework and will be executed; but it will not be reported in console or html report. This was created on a request by a user; but I personally do not use this feature. If I need to setup my state I either do it in the 'Given' steps or in the class constructor if it is not directly related to the scenario state.
-It is worth mentioning that these conventions can be easily overridden (but not a topic of this post).
+It is worth mentioning that these conventions can be easily overridden if your needs require further customisation.
-BDDfy by default uses your scenario class name to generate a title for your scenario. You may easily override this behavior too as we will see further down.
+BDDfy by default uses your scenario class name to generate a title for your scenario, however you can easily override this behaviour as we will see further down.
##Another example
Let's expand on the example above and create something a bit more complex. My specification this time reads as:
@@ -125,7 +127,7 @@ namespace BDDfy.MethodNameConventions
}
-Let's run this guy. This time I use [TD.Net](http://www.testdriven.net/) to show you the result from another test runner:
+Let's run this. This time I use [TD.Net](http://www.testdriven.net/) to show you the result from another test runner:

@@ -181,12 +183,12 @@ This runs the WinnerGame test class as several scenarios with diffe
So far we have been calling BDDfy() with no arguments so you may wonder what the title argument does. As you may guess from its name that argument overrides the scenario title. If we had not passed that argument in we would end up with 7 scenarios all titled 'Winner game' which is not what we want. So we pass in the title we want for the scenario based on the input arguments.
##FAQ
-These are some of the FAQ I have received for Method Name Conventions:
+These are some of the FAQs I have received for Method Name Conventions:
#####Should I have my methods in the right order?
-No you do not. BDDfy picks the methods based on the naming convention and regardless of where in the class they appear BDDfy runs and reports them in the right order.
+Ordinarily, no. BDDfy picks the methods based on the naming convention and regardless of where in the class they appear BDDfy runs and reports them in the right order. However, if you have multiple 'AndGiven', 'AndWhen', or 'And' steps you need to put these methods in the order that you want BDDfy to pick them up.
-There is only one rare case where you need to put some of your methods in the right order and that is when you have multiple 'AndGiven' or 'AndWhen' or 'And' steps in which case BDDfy picks up the 'And' steps in the order they are written in the class.
+If for some reason you are using both a Setup method and an xxxContext method to perform some setup then these will run in the order in which they are defined in your class. It is probably a better idea to refactor these into a single method if possible.
#####How I can reuse some of the testing logic?
You may achieve that through scenario inheritance or composition as you would in your business logic code.
@@ -201,6 +203,5 @@ Because you should define them either as public or protected. BDDfy ignores the
#####Can my step methods be static or should they be instance methods?
BDDfy handles both cases. So feel free to use whatever makes sense.
-#####Where can I setup my mocks or other bits not directly related to the scenario?#####
-When unit testing you usually end up mocking a few interfaces and setting up a few things that are not necessarily related to the scenario under test; but are necessary for you to be able to test the scenario. I usually put this logic into the class constructor. If what you are setting up is directly related to the scenario then you should put the logic in your 'Given' step(s).
-
+#####Where can I setup my mocks or other bits not directly related to the scenario?
+When unit testing you usually end up mocking a few interfaces and setting up a few things that are not necessarily related to the scenario under test, but are necessary for you to be able to test the scenario. I usually put this logic into the class constructor. If what you are setting up is directly related to the scenario then you should put the logic in your 'Given' step(s).
diff --git a/_source/BDDfy/Usage/Reports.md b/_source/BDDfy/Usage/Reports.md
new file mode 100644
index 0000000..73f7dd8
--- /dev/null
+++ b/_source/BDDfy/Usage/Reports.md
@@ -0,0 +1,128 @@
+---
+layout: layout
+title: Reports
+order: 6
+---
+
+BDDfy provides a rich array of reports to choose from and is also very easy to extend if you want to add your own reports. The two main reports are the Console Report and the HTML Report and both of these are on by default, though you can turn them off if you want to. In addition there is a Markdown Report, and recently a Diagnostics Report using JSON has been added. Both of these are off by default.
+
+##The Reports
+I am going to start with an overview of the different reports available in BDDfy, using the ATM sample, available in the [BDDfy source code](https://github.com/TestStack/TestStack.BDDfy/tree/master/Samples/TestStack.BDDfy.Samples/Atm), or on [NuGet][3].
+
+###Console Report
+The Console Report is what provides feedback in Visual Studio when you run your tests. If you run the tests with TestDriven.Net then you will see the output from all the tests in the output window.
+
+
+
+At the end of the report it will also provide a summary of how many tests passed, failed, or were skipped and how long the tests took to run.
+
+
+
+If you run the tests in ReSharper then you see the output of each test individually when you select it in the Unit Test Sessions window.
+
+
+
+When the test passes, you just see the Scenario listed out, and its story if it has one. If the test fails, or is not implemented yet, then you will also see details alongside each step of which steps were executed and what their status was and an exception trace detailing the error information.
+
+
+
+###HTML Report
+If you are practicing BDD, then you will probably be interested in living documentation. BDDfy can help with this with its HTML report, which dev teams can share with their customers to see the progress in a very user friendly and accessible way. Every time you run tests with BDDfy it creates an HTML report in the bin directory of the test project. The report has a summary at the top, listing out how many Stories/Scenarios have run, and the totals for each type of execution result. The report is interactive, and lets you expand and collapse individual stories and scenarios or all at once. The report is very customisable and you are able to change the header, description, and the location where the report is saved to. You can also add your own CSS and JavaScript files to really open up the customisation possibilities.
+
+
+
+###Markdown Report
+The Markdown Report can be turned on using the BDDfy Configurator (more on that below). The report is written in the [GitHub Flavoured Markdown][9] format. Markdown is a really useful format for documenting (I write this blog in markdown). A possible use for this would be as part of efforts to generate documentation, which might be particularly useful for open source developers to generate wiki documents from their code, for example.
+
+The Markdown Report is output to the bin directory of the test project and is named BDDfy.md. The picture below shows the BDDfy.md file in the MarkPad markdown editor. The left pane shows the raw text view and the pane on the right shows how it would be displayed on a web page.
+
+
+
+###Diagnostics Report
+The Diagnostics Report is the most recent addition to the BDDfy stable. It is also off by default and can be turned on using the BDDfy Configurator. In BDDfy we can measure how long every step took to execute and then aggregate that data to see how long each Scenario and Story took to execute. This is particularly useful information if you have long running tests, such as browser-based functional tests, and want to identify the parts of the test that are having the worst impact on performance. For example, is it particularly slow when interacting with the database, or is it perhaps the rendering of the web pages?
+
+The Diagnostics Report is created in the JSON format. This is useful if you want to load the data into another system, perhaps to persist test runs to compare performance over time. The Diagnostics Report is output to the bin directory of the test project and is named Diagnostics.json. Here is the output for the ATM tests.
+
+ {
+ "Stories":
+ [
+ {
+ "Name":"Account holder withdraws cash",
+ "Duration":8,
+ "Scenarios":
+ [
+ {
+ "Name":"Account has insufficient fund",
+ "Duration":8,
+ "Steps":
+ [
+ {
+ "Name":"Given the Account Balance is $10",
+ "Duration":1
+ },
+ {
+ "Name":"And the Card is valid",
+ "Duration":0
+ },
+ {
+ "Name":"And the machine contains enough money",
+ "Duration":0
+ },
+ {
+ "Name":"When the Account Holder requests $20",
+ "Duration":0
+ },
+ {
+ "Name":"Then the ATM should not dispense any Money",
+ "Duration":5
+ },
+ {
+ "Name":"And the ATM should say there are Insufficient Funds",
+ "Duration":0
+ },
+ {
+ "Name":"And the Account Balance should be $20",
+ "Duration":0
+ },
+ {
+ "Name":"And the Card should be returned",
+ "Duration":0
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+
+
+##Configuring Reports
+
+The Configurator class is the main configuration point for BDDfy and should be called before all your tests run if you are wanting to change the default behaviour. For example, in NUnit you could call it from the SetUpFixture.
+
+BDDfy implements components as processors in a pipeline (using the [Chain of Responsibility pattern][11]) and reports are just another type of processor. Processors can be switched on and off using the Configurator class by calling the Enable or Disable methods. As previously mentioned, the Console Report and the HTML Report are both on by default. If you don’t want them to run then you can disable them like this:
+
+ Configurator.Processors.ConsoleReport.Disable();
+ Configurator.BatchProcessors.HtmlReport.Disable();
+
+Similarly, you can turn on the Markdown and Diagnostics reports:
+
+ Configurator.BatchProcessors.MarkDownReport.Enable();
+ Configurator.BatchProcessors.DiagnosticsReport.Enable();
+
+While this is great if you want to turn a processor off for all the tests, it isn’t much help if you want to just turn it on or off for some of the tests. Fortunately, there is also the the RunsOn method, which allows you to enable or disable processors using a predicate. This allows a lot of flexibility, and you could even choose to combine predicates so that, for example, half the tests ran with the Console Report and the other half ran with the Markdown Report.
+
+ Configurator.Processors.ConsoleReport
+ .RunsOn(scenario => scenario.GetType().Namespace.StartsWith("MyCompany.MyApp.Domain"));
+ Configurator.Processors.MarkdownReport
+ .RunsOn(scenario => !scenario.GetType().Namespace.StartsWith("MyCompany.MyApp.Domain"));
+
+###Getting reports without running the tests
+Having processors run in a pipeline leads to some interesting possibilities. One that I particularly like is that you can get all of the reports without actually running the tests. To do this you just need to turn off the TestRunner processor. The reports will still be generated, the only difference is that they will have a status of Not Executed!
+
+ Configurator.Processors.TestRunner.Disable();
+
+This is really useful when you want to print out the reports as documentation but don’t want to have to wait for the tests to run.
+
+
+
diff --git a/_source/BDDfy/Usage/index.md b/_source/BDDfy/Usage/index.md
index 17f60dd..b0bacaf 100644
--- a/_source/BDDfy/Usage/index.md
+++ b/_source/BDDfy/Usage/index.md
@@ -1,6 +1,7 @@
---
layout: layout
title: Usage
+order: 1
---
See menu items to the left for Bddfy usage instructions.
diff --git a/_source/ConventionTests/Index.md b/_source/ConventionTests/Index.md
index 6250de9..e50cb1f 100644
--- a/_source/ConventionTests/Index.md
+++ b/_source/ConventionTests/Index.md
@@ -1,47 +1,65 @@
---
+
layout: layout
-title: ConventionTests
+
+title: ConventionTests
---
## What is ConventionTests?
-Convention over Configuration is a great way to cut down repetitive boilerplate code. But how do you validate that your code adheres to your conventions? Convention Tests is a code-only NuGet that provides a simple API to build validation rules for convention validation tests.
+Convention over Configuration is a great way to cut down repetitive boilerplate code. But how do you validate that your code adheres to your conventions?
+
+ConventionTests provides a simple API to build validation rules for creating convention validation tests.
## Getting Started
-It is really easy to get started with Convention Tests, we have included a bunch of conventions out of the box. The included conventions are:
+It is really easy to get started with ConventionTests, there are a number of included conventions that come out of the box:
- All Classes Have Default Constructor
- All Methods are Virtual
- Class type has specific namespace (for example, all dtos must live in the ProjectName.Dtos namespace)
- Files are Embedded Resources
- Project does not reference dlls from Bin or Obj directory
- - Plus others will be added!
+ - Others are being added over time and you can create your own custom ones
### Writing your first Convention test
-#### 1. Using your favourite testing framework, create a new test. Lets call it `nhibernate_entities_must_have_default_constructor`
-#### 2. Define some data
-At the moment there is minimal support for type scanning, but better support will be added soon!
+#### 1. Using your favourite testing framework, create a new test. Lets call it:
+
+`entities_must_have_default_constructor`
- var itemsToVerify = typeof (SampleDomainClass).Assembly.GetTypes();
- var nhibernateEntities = new Types("nHibernate Entitites")
- {
- TypesToVerify = itemsToVerify
- };
+#### 2. Define some types to validate against a convention
+
+The following line get a list of all the types in the assembly that contains `SampleDomainClass`.
+
+ var itemsToVerify = Types.InAssemblyOf();
+There are also overloads to restrict the types to a specific namespace within the assembly.
+
#### 3. Assert the convention
-`Convention.Is(new AllClassesHaveDefaultConstructor(), nhibernateEntities);`
+
+Now we have a list of types to check, we can use one of the pre-built conventions and check all of the types against this convention:
+
+`Convention.Is(new AllClassesHaveDefaultConstructor(), itemsToVerify);`
#### That's it!
-When you run this convention, if it fails an exception will be thrown, which will look something like this:
+
+When you run this convention, if any of the types don't meet with the chosen convention, it fails and an exception will be thrown, which will look something like this:
ConventionFailedException
- Message = Failed: 'Types must have a default constructor' for 'nHibernate Entitites'
- --------------------------------------------------------------------------
+ Message = Failed: 'Types must have a default constructor'
+ -----------------------------------------------------------------
TestAssembly.ClassWithNoDefaultCtor
TestAssembly.ClassWithPrivateDefaultCtor
How cool is that!
-Also, if you look in the directory where your test assembly is, there will be an html report called `Conventions.htm`, this will list all your projects conventions and serve as living documentation!
\ No newline at end of file
+### Reporting
+If you would like to use ConventionTests reporting features, you just have to opt in by specifying the reporter you want. This makes it easy to add your own reporters, for example a WikiReporter may be better than the `HtmlReporter`
+
+In your `Properties\AssemblyInfo.cs` file add the reporters you want. This are global reporters which will report the results of all conventions.
+
+ [assembly: ConventionReporter(typeof(HtmlConventionResultsReporter))]
+ [assembly: ConventionReporter(typeof(MarkdownConventionResultsReporter))]
+
+Then if you look in the directory where your test assembly is, there will be an html report called `Conventions.htm`, serving as living documentation!
diff --git a/_source/FluentMVCTesting/usage.md b/_source/FluentMVCTesting/usage.md
index 4b6d74d..5e040f1 100644
--- a/_source/FluentMVCTesting/usage.md
+++ b/_source/FluentMVCTesting/usage.md
@@ -139,10 +139,32 @@ Unfortunately, we couldn't think of a way to get rid of the magic strings here s
See below for view model testing.
### Files
+ _controller.WithCallTo(c => c.Index()).ShouldRenderAnyFile();
+ _controller.WithCallTo(c => c.Index()).ShouldRenderAnyFile("content/type");
- _controller.WithCallTo(c => c.Index()).ShouldRenderFile();
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFileContents();
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFileContents(new byte[] {1});
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFileContents(new byte[] {1}, "content/type");
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFileContents("text");
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFileContents("text", "content/type");
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFileContents("text", "content/type", Encoding.Unicode);
- _controller.WithCallTo(c => c.Index()).ShouldRenderFile("content/type");
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFileStream();
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFileStream(new byte[] {1});
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFileStream(new byte[] {1}, "content/type");
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFileStream(new MemoryStream());
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFileStream(new MemoryStream(), "content/type");
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFileStream("text");
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFileStream("text", "content/type");
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFileStream("text", "content/type", Encoding.Unicode);
+
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFilePath();
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFilePath("file name");
+ _controller.WithCallTo(c => c.Index()).ShouldRenderFilePath("file name", "content/type");
+
+It is useful to note that whilst file content is ultimately represented in memory as binary data, we enable you to test for textual content using convenience overloads.
+
+In order achieve this - internally we reconstitute the text by assuming that the file content is UTF8 encoded. If you know our assumption to be wrong then you must explicitly tell the test method the actual file content encoding by means of the apparent `encoding` argument as not to cause a misleading test failure.
### Http status codes
@@ -160,6 +182,25 @@ See below for view model testing.
Assert.That(data.SomeProperty, Is.EqualTo("SomeValue");
});
+### Content
+
+ _controller.WithCallTo(c => c.Index()).ShouldReturnContent();
+ _controller.WithCallTo(c => c.Index()).ShouldReturnContent("expected content");
+ _controller.WithCallTo(c => c.Index()).ShouldReturnContent("expected content", "content/type");
+ _controller.WithCallTo(c => c.Index()).ShouldReturnContent("expected content", "content/type", Encoding.UTF8);
+
+### TempData
+
+ _controller.Index();
+
+ _controller.ShouldHaveTempDataProperty("Message")
+ _controller.ShouldHaveTempDataProperty("Message", "Welcome to the website.")
+ _controller.ShouldHaveTempDataProperty("Message", m => m.Contains("Welcome"))
+
+It is important to note that the compiler cannot infer the generic type argument based on the given lambda expression and so you must explicitly supply a generic type argument in all cases.
+
+It may also be important to note that the temp data test methods break the convention slightly by defining test methods directly on the `Controller` instead of on the immediate result of `WithCallTo`. This is slightly counter-intuitive at first, but ultimately, it enables you to be more dynamic when writing your tests.
+
### Model tests
If you assert that the action returns a view of some sort there are some other methods that you can call (seen easily using intellisense). These allow you to check the model, e.g.:
diff --git a/_source/Guidance/PageObjectPattern.md b/_source/Guidance/PageObjectPattern.md
index ba4465a..d6f6bcc 100644
--- a/_source/Guidance/PageObjectPattern.md
+++ b/_source/Guidance/PageObjectPattern.md
@@ -3,10 +3,34 @@ layout: layout
title: Page Object Pattern
---
-The Page Object Pattern is a well known pattern in UI Automation testing, and is supported by both White and Seleno (though each has it's own implementation which is covered in the docs of each project specifically).
+The Page Object Pattern is a well known pattern to make life easier for us when performing automated UI testing.
-When UI Testing, it is easy to think procedurally. If you write all your tests as a series of interactions with elements on a page, or in a window, your tests will be brittle and hard to maintain.
+This pattern is supported by both White and Seleno, each has it's own implementation which is covered in their documentation pages.
-Page objects give logical representations of your the pages/screens in your applications as well as reusable components in those pages/screens to allow you to reuse interaction logic and when the UI changes, you only have to fix your tests in a single place.
+The use of the Page Objects provides a number of benefits:
-**in progress - this article will be expanded on in the future**
\ No newline at end of file
+- Less code duplication
+- Lower long-term maintenance costs
+- More readable and understandable test scenarios
+
+##So What Is A Page Object?
+
+Put simply, it is a class that models some of the UI items on a given screen or page in the application. In this way we are modelling what the page looks like to the end-user and thinking from their perspective, rather than from an internal implementation perspective.
+
+This page object may contain properties that represent items on the screen, such as a string property that represents an order item quantity. This quantity property abstract away the actual UI automation code. Test code can them simply make use of this quantity property whenever it needs to read or set the quantity in the screen.
+
+##Modelling Logical User Functions
+
+In addition to simple properties to get/set individual UI control values, methods can also be added to a page object. These methods represent logical actions the end-user can make in the UI.
+
+For example, a method LoginAs(string name, string password) could be added that represents a login screen. Internally this LoginAs method types the username and password into the UI and then clicks the login button.
+
+Test code can now simply call the LoginAs method.
+
+Now any tests that login, can reuse this method. The intent of the tests also becomes clearer as the flow of the test is not obscured with many individual UI get/sets/clicks/etc.
+
+##Maintenance Benefits
+
+Because UI interaction has been abstracted away from test code, when the UI changes only the page objects have to be updated.
+
+For example, if the ID of the login button changed, only the login screen POM would need to be updated. Without page objects, every single test would be specifying this ID and would need to be changed individually.
diff --git a/_source/Seleno/ActionsAndLocators.md b/_source/Seleno/ActionsAndLocators.md
index 1fc828f..c19c066 100644
--- a/_source/Seleno/ActionsAndLocators.md
+++ b/_source/Seleno/ActionsAndLocators.md
@@ -8,35 +8,35 @@ Page Objects tend to either perform actions or find items on the page. Seleno Pa
## Actions ##
### Navigator ###
-Used to perform actions that take you to another page, such as clicking a button or a link, or navigating to a URL. There is a strongly typed option to navigate with a controller expression using routing. Page Objects expose the Navigator class with the Navigate() method.
+Used to perform actions that take you to another page, such as clicking a button or a link, or navigating to a URL. There is a strongly typed option to navigate with a controller expression using routing. Page Objects expose the Navigator class with the `Navigate` property.
- Navigate().To(By.LinkText("Register"));
+ Navigate.To(By.LinkText("Register"));
### Page Reader ###
-Read values from the page using view model based strongly typed methods. Page Objects that extend `Page`expose the Page Reader class with the Read() method. For example, to read all fields into a new instance of the current model type (`T`) in a page you can use the `ModelFromPage` method:
+Read values from the page using view model based strongly typed methods. Page Objects that extend `Page`expose the Page Reader class with the `Read` property. For example, to read all fields into a new instance of the current model type (`T`) in a page you can use the `ModelFromPage` method:
- var model = Read().ModelFromPage();
+ var model = Read.ModelFromPage();
-Read() is currently only available with the generically-typed page object for now, but if you want this functionality with non-generic page objects (by instead supplying strings for the id) then to add an issue to our Github site so we can prioritise it.
+`Read` is currently only available with the generically-typed page object for now, but if you want this functionality with non-generic page objects (by instead supplying strings for the id) then to add an issue to our Github site so we can prioritise it.
### Page Writer ###
-Write values to the page using view model based strongly typed methods. Page Objects that extend `Page`expose the Page Writer class with the Input() method. For example, to write all fields from a model of the current type (`T`) from the form on a page you can use the `Model` method:
+Write values to the page using view model based strongly typed methods. Page Objects that extend `Page`expose the Page Writer class with the `Input` property. For example, to write all fields from a model of the current type (`T`) from the form on a page you can use the `Model` method:
- Input().Model(modelInstanceToFillInFormUsing);
+ Input.Model(modelInstanceToFillInFormUsing);
-Input() is currently only available with the generically-typed page object for now, but if you want this functionality with non-generic page objects (by instead supplying strings for the id) then to add an issue to our Github site so we can prioritise it.
+`Input` is currently only available with the generically-typed page object for now, but if you want this functionality with non-generic page objects (by instead supplying strings for the id) then to add an issue to our Github site so we can prioritise it.
### Script Executor ###
-If none of the above meet your needs then you have the ultimate control by executing JavaScript with the Script Executor class. Page Objects expose the Script Executor class with the Execute() method.
+If none of the above meet your needs then you have the ultimate control by executing JavaScript with the Script Executor class. Page Objects expose the Script Executor class with the `Execute` property.
- return Execute().ScriptAndReturn(string.Format("$('#{0}').attr('{1}')",Id,attributeName));
+ return Execute.ScriptAndReturn(string.Format("$('#{0}').attr('{1}')",Id,attributeName));
## Locators ##
### Element Finder ###
-Finds Selenium IWebElement items on the page, using the Selenium By selectors or the Seleno jQuery selectors. Page Objects expose the Element Finder class with the Find() method.
+Finds Selenium IWebElement items on the page, using the Selenium By selectors or the Seleno jQuery selectors. Page Objects expose the Element Finder class with the `Find` property.
var selector = string.Format("$('#{0} option:selected')", Id);
- return Find().Element(By.jQuery(selector), WaitInSecondsUntilElementAvailable);
+ return Find.Element(By.jQuery(selector), WaitInSecondsUntilElementAvailable);
# Navigation #
A great way to slow down your tests is to start each test on the home page and then navigate to the page you want to test! It's much better to navigate directly to the page that you want to test. You can do this by calling the NavigateToInitialPage method on SelenoHost instance and passing in the *relative* URL (to the root of the site being tested) or an absolute URL.
diff --git a/_source/Seleno/Configuration.md b/_source/Seleno/Configuration.md
index 92c47d0..6ef7e67 100644
--- a/_source/Seleno/Configuration.md
+++ b/_source/Seleno/Configuration.md
@@ -33,7 +33,9 @@ If you want to change these defaults, you can use a fluent configuration to over
.UsingLoggerFactory(new ConsoleFactory())
);
-> Note: To use the Chrome WebDriver you also need to download [ChromeDriver.exe](https://code.google.com/p/selenium/wiki/ChromeDriver) and make sure it is in your bin directory when you run your tests.
+> Note: To use the Chrome WebDriver you also need to download [ChromeDriver.exe](https://code.google.com/p/selenium/wiki/ChromeDriver) and make sure it is in your bin directory when you run your tests. Once you install the NuGet package you can also type `Install-ChromeDriver` into the NuGet package manager console and this will install the driver for you.
+
+
There are more things that you can configure too. The original use case for Seleno was to test Visual Studio web projects for ASP.Net and ASP.Net MVC and it defaults to doing this with IIS Express. Seleno attempts to be modular and easy to customise though, so to test any website instead is just a matter of swapping out the IisExpressWebServer for the InternetWebServer. For example, to test Google UK:
diff --git a/_source/Seleno/PageObjects.md b/_source/Seleno/PageObjects.md
index 1fcd897..b8f9314 100644
--- a/_source/Seleno/PageObjects.md
+++ b/_source/Seleno/PageObjects.md
@@ -6,7 +6,7 @@ order: 1
Seleno supports the idea of [Page Objects](https://code.google.com/p/selenium/wiki/PageObjects) to model your web pages. The idea is to model each page (or part of a page) as a class, and then to use instances of those classes in your tests. To continue, with the google example, we're all familiar with this page.
-
+
In Seleno, you would model this page by inheriting from the Page class and then encapsulating the interaction with the page within methods. Seleno provides methods to find elements on the page (Find) and enter data into the text box (SendKeys). The Navigate method allows you to navigate to the results page by clicking on the Search button (btnG).
@@ -14,15 +14,14 @@ In Seleno, you would model this page by inheriting from the Page class and then
{
public SearchPage InputSearchTerm(string term)
{
- Find()
- .Element(By.Name("q"))
+ Find.Element(By.Name("q"))
.SendKeys(term);
return this;
}
public ResultsPage Search()
{
- return Navigate().To(By.Name("btnG"));
+ return Navigate.To(By.Name("btnG"));
}
}
@@ -73,8 +72,8 @@ That's quite nice, but things start to get really interesting if you use view mo
{
public HomePage CreateValidUser(RegisterModel model)
{
- Input().Model(model);
- return Navigate().To(By.CssSelector("input[type='submit']"));
+ Input.Model(model);
+ return Navigate.To(By.CssSelector("input[type='submit']"));
}
}
@@ -99,4 +98,8 @@ This can then be used in a test, with a populated model generated by the CreateR
orderedPage.Title.Should().Be("Checkout Complete");
}
- }
\ No newline at end of file
+ }
+
+Notice here the `Host.Instance.NavigateToInitialPage(x => x.Index())` allows us to specify navigation by selecting a controller and action (rather than a Url string). Before using this method be sure to register the application routes when you are initializing Seleno, e.g.:
+
+ .Run("MvcMusicStore", 12345, c => c.WithRouteConfig(RouteConfig.RegisterRoutes));
\ No newline at end of file
diff --git a/_source/White/GettingStarted.md b/_source/White/GettingStarted.md
index 9ca5caf..332f507 100644
--- a/_source/White/GettingStarted.md
+++ b/_source/White/GettingStarted.md
@@ -1,13 +1,13 @@
----
-layout: layout
-title: Getting started with White
-order: 1
----
-
-### Pre-requisites
-.NET 4.0 framework
-
-## Getting Started
+---
+layout: layout
+title: Getting started with White
+order: 1
+---
+
+### Pre-requisites
+.NET 4.0 framework
+
+## Getting Started
1. Install via NuGet
`> install-package TestStack.White`
1. Have a look at the [WPF](https://github.com/TestStack/White/tree/master/src/Sample%20App/Wpf) or [WinForms](https://github.com/TestStack/White/tree/master/src/Sample%20App/WinForms) sample projects
@@ -21,23 +21,71 @@ order: 1
## Writing your first test
### Start off with an empty test stub
- [Fact]
- public void MyFirstUITest() { }
+
+In *MSTest*:
+
+ [TestClass]
+ public class MyTests
+ {
+ [TestMethod]
+ public void MyFirstUITest()
+ {
+ }
+ }
+
+In *NUnit*:
+
+ [TestFixture]
+ public class MyTests
+ {
+ [Test]
+ public void MyFirstUITest()
+ {
+ }
+ }
+
+In *xUnit*:
+
+ public class MyTests
+ {
+ [Fact]
+ public void MyFirstUITest()
+ {
+ }
+ }
### Get hold of a window
- Application application = Application.Launch("foo.exe");
- Window window = application.GetWindow("bar", InitializeOption.NoCache);
+First you need to determine the correct path of the application you want to test.
+
+In *MSTest*:
+
+ var applicationDirectory = TestContext.TestDeploymentDir
+
+In *NUnit*:
+
+ var applicationDirectory = TestContext.CurrentContext.TestDirectory;
+
+Then you create a new instance of your application
+
+ var applicationPath = Path.Combine(applicationPath, "foo.exe");
+ Application application = Application.Launch(applicationPath);
+ Window window = application.GetWindow("bar", InitializeOption.NoCache);
White uses the UI Automation API (UIA) to find controls on a window. UIA communicates to a displayed window via window messages. This find is performed by iterating through all the controls in a window.
### Finding a UI Item and performing action
- Button button = window.Get