Skip to content

Commit 6e797fb

Browse files
committed
Merge pull request #213 from JakeGinnivan/BuildUpdates
Build updates
2 parents 29a22a8 + 110a677 commit 6e797fb

File tree

296 files changed

+2981
-157
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

296 files changed

+2981
-157
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ TestResult.xml
1818
TestStack.BDDfy.sln.ide/graph
1919
_NCrunch_TestStack.BDDfy/
2020
TestStack.BDDfy.sln.ide/
21+
src/packages/

TestStack.BDDfy.Samples.nuspec

Lines changed: 0 additions & 19 deletions
This file was deleted.

appveyor.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
install:
2+
- choco install gitversion.portable -pre -y
3+
4+
platform:
5+
- Any CPU
6+
7+
configuration:
8+
- Release
9+
10+
assembly_info:
11+
patch: false
12+
13+
before_build:
14+
- nuget restore src\
15+
- ps: gitversion /l console /output buildserver /updateAssemblyInfo
16+
17+
build:
18+
project: src\TestStack.BDDfy.sln
19+
20+
after_build:
21+
- cmd: nuget pack src\TestStack.BDDfy\TestStack.BDDfy.nuspec -BasePath src\TestStack.BDDfy\ -version "%GitVersion_NuGetVersion%" -prop "configuration=%CONFIGURATION%"
22+
- cmd: appveyor PushArtifact "TestStack.BDDfy.%GitVersion_NuGetVersion%.nupkg"
23+
24+
- cmd: 7z a "TestStack.BDDfy_%GitVersion_NuGetVersion%.zip" -r src\TestStack.BDDfy\bin\%CONFIGURATION%\*.*
25+
- cmd: appveyor PushArtifact "TestStack.BDDfy_%GitVersion_NuGetVersion%.zip"
26+
27+
cache:
28+
- src\packages -> **\packages.config # preserve "packages" directory in the root of build folder but will reset it if packages.config is modified
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Architecture Overview
2+
## Introduction ##
3+
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.
4+
5+
![BDDfy functional decomposition](/img/BDDfy/Customizing/bddfy-functional-decomposition.png)
6+
7+
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.
8+
9+
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.
10+
11+
## Scanners ##
12+
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:
13+
14+
![BDDfy scanners](/img/BDDfy/Customizing/bddfy-scanners.png)
15+
16+
### Story Scanner ###
17+
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).
18+
19+
The Story Metadata Scanner gets information from the Story attribute, if one exists on the class.
20+
21+
A **Story** has the following properties:
22+
23+
- **Story Metadata**: Information about the Story such as Title, As a, I want, So that
24+
- **Scenarios**: The collection of Scenarios related to the Story
25+
- **Result**: A Story’s Result is a Step Execution Result and is determined by the highest Step Execution Result of its Scenarios.
26+
- **Category**: The Story Category
27+
28+
**Step Execution Results** have a numerical hierarchy and can be (in ascending order):
29+
30+
- Not Executed (0)
31+
- Passed (1)
32+
- Not Implemented (2)
33+
- Inconclusive (3)
34+
- Failed (4)
35+
36+
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.
37+
38+
### Scenario Scanners ###
39+
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.
40+
41+
A **Scenario** has the following properties:
42+
43+
- **Title**: The Scenario Title
44+
- **Steps**: The collection of Steps (test class methods) related to the Scenario
45+
- **Result**: A Scenario’s Result is a Step Execution Result and is determined by the highest Step Execution Result of its Steps.
46+
- **Duration**: How long the Scenario took to execute. Used by Diagnostics.
47+
48+
### Step Scanners ###
49+
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.
50+
51+
An **Execution Step** has the following properties:
52+
53+
- **Title**: The Step Title
54+
- **Result**: The result of executing the Step.
55+
- **Duration**: How long the Step took to execute. Used by Diagnostics.
56+
- **Asserts**: Whether or not the Step is an Assertion Step.
57+
- **Should Report**: Whether the Step should be displayed in reports.
58+
- **Execution Order**: The order that the step should run in relative to the other steps. Can be (in ascending order)
59+
- Initialize (for example, "Context", "Setup)
60+
- Setup State ("Given")
61+
- Consecutive Setup State ("And Given")
62+
- Transition ("When)
63+
- Consecutive Transition "And When")
64+
- Assertion ("Then")
65+
- Consecutive Assertion ("And Then")
66+
- Tear Down ("TearDown")
67+
68+
## Processors ##
69+
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.
70+
71+
The various **Process Types**, in order, are:
72+
73+
1. Firstly
74+
1. Execute
75+
1. Before Report
76+
1. Report
77+
1. After Report
78+
1. Process Exceptions
79+
1. Finally
80+
81+
![BDDfy processor pipeline](/img/BDDfy/Customizing/bddfy-processor-pipeline.png)
82+
83+
84+
1. **Test Runner (Execute)**: Executes the tests.
85+
1. **Console Reporter (Report)**: Displays the test result in the console.
86+
1. **Exception Processor (Process Exceptions)**: Handles exceptions.
87+
1. **Story Cache (Finally)**: Saves each Story for later processing by the Batch Processors.
88+
1. **Disposer (Finally)**: Cleans up the Story and its Scenarios.
89+
90+
## Batch Processors ##
91+
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.
92+
93+
![BDDfy batch processor pipeline](/img/BDDfy/Customizing/bddfy-batch-processor-pipeline.png)
94+
95+
- **HTML Reporter**: Creates the HTML report
96+
- **Markdown Reporter**: Creates the Markdown report
97+
- **Diagnostics Reporter**: Creates the Diagnostics report
98+
99+
## Configurator ##
100+
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.
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# Customizing Reports
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).
3+
4+
## Customizing the HTML Report ##
5+
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.
6+
7+
public interface IHtmlReportConfiguration
8+
{
9+
string ReportHeader { get; }
10+
string ReportDescription { get; }
11+
string OutputPath { get; }
12+
string OutputFileName { get; }
13+
bool RunsOn(Story story);
14+
}
15+
16+
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).
17+
18+
public class HtmlReportConfig : DefaultHtmlReportConfiguration
19+
{
20+
public override bool RunsOn(Core.Story story)
21+
{
22+
return story.MetaData.Type.Namespace != null && story.MetaData.Type.Namespace.EndsWith("Atm");
23+
}
24+
25+
/// <summary>
26+
/// Change the output file name
27+
/// </summary>
28+
public override string OutputFileName
29+
{
30+
get
31+
{
32+
return "ATM.html";
33+
}
34+
}
35+
36+
/// <summary>
37+
/// Change the report header to your project
38+
/// </summary>
39+
public override string ReportHeader
40+
{
41+
get
42+
{
43+
return "ATM Solutions";
44+
}
45+
}
46+
47+
/// <summary>
48+
/// Change the report description
49+
/// </summary>
50+
public override string ReportDescription
51+
{
52+
get
53+
{
54+
return "A reliable solution for your offline banking needs";
55+
}
56+
}
57+
}
58+
59+
which produces the following customised report, which you will find in your bin directory named ATM.html:
60+
61+
![BDDfy functional decomposition](/img/BDDfy/Customizing/bddfy-sample-atm-html-custom.png)
62+
63+
64+
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.
65+
66+
Configurator.BatchProcessors.Add(new HtmlReporter(new HtmlReportConfig()));
67+
68+
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.
69+
70+
Configurator.BatchProcessors.HtmlReport.Disable();
71+
72+
## Custom CSS and JavaScript ##
73+
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.
74+
75+
76+
## Create a custom report by creating a new Processor ##
77+
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.
78+
79+
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/).
80+
81+
/// <summary>
82+
/// This is a custom reporter that shows you how easily you can create a custom report.
83+
/// Just implement IProcessor and you are done
84+
/// </summary>
85+
public class CustomTextReporter : IProcessor
86+
{
87+
private static readonly string Path;
88+
89+
private static string OutputDirectory
90+
{
91+
get
92+
{
93+
string codeBase = typeof(CustomTextReporter).Assembly.CodeBase;
94+
var uri = new UriBuilder(codeBase);
95+
string path = Uri.UnescapeDataString(uri.Path);
96+
return System.IO.Path.GetDirectoryName(path);
97+
}
98+
}
99+
100+
static CustomTextReporter()
101+
{
102+
Path = System.IO.Path.Combine(OutputDirectory, "BDDfy-text-report.txt");
103+
104+
if(File.Exists(Path))
105+
File.Delete(Path);
106+
107+
var header =
108+
" A custom report created from your test assembly with no required configuration " +
109+
Environment.NewLine +
110+
Environment.NewLine +
111+
Environment.NewLine +
112+
Environment.NewLine;
113+
File.AppendAllText(Path, header);
114+
}
115+
116+
public void Process(Story story)
117+
{
118+
// use this report only for tic tac toe stories
119+
if (!story.MetaData.Type.Name.Contains("TicTacToe"))
120+
return;
121+
122+
var scenario = story.Scenarios.First();
123+
var scenarioReport = new StringBuilder();
124+
scenarioReport.AppendLine(string.Format(" SCENARIO: {0} ", scenario.Title));
125+
126+
if (scenario.Result != StepExecutionResult.Passed && scenario.Steps.Any(s => s.Exception != null))
127+
{
128+
scenarioReport.Append(string.Format(" {0} : ", scenario.Result));
129+
scenarioReport.AppendLine(scenario.Steps.First(s => s.Result == scenario.Result).Exception.Message);
130+
}
131+
132+
scenarioReport.AppendLine();
133+
134+
foreach (var step in scenario.Steps)
135+
scenarioReport.AppendLine(string.Format(" [{1}] {0}", step.StepTitle, step.Result));
136+
137+
scenarioReport.AppendLine("--------------------------------------------------------------------------------");
138+
scenarioReport.AppendLine();
139+
140+
File.AppendAllText(Path, scenarioReport.ToString());
141+
}
142+
143+
public ProcessType ProcessType
144+
{
145+
get { return ProcessType.Report; }
146+
}
147+
}
148+
149+
This produces the BDDfy-text-report.txt text file report which is output to the bin directory:
150+
151+
![BDDfy custom text report](/img/BDDfy/Customizing/bddfy-custom-text-report.png)
152+
153+
## Create a custom report by creating a new Batch Processor ##
154+
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.
155+
156+
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.
157+
158+
public class MyConsoleReporter : IBatchProcessor
159+
{
160+
public void Process(IEnumerable<Story> stories)
161+
{
162+
var reporter = new ConsoleReporter();
163+
stories
164+
.ToList()
165+
.ForEach(story => reporter.Process(story));
166+
}
167+
}
168+
169+
Then I just needed to add it to the Batch Processor pipeline and disable the built-in console report:
170+
171+
Configurator.Processors.ConsoleReport.Disable();
172+
Configurator.BatchProcessors.Add(new MyConsoleReporter());
173+
174+
175+
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.

0 commit comments

Comments
 (0)