-
Notifications
You must be signed in to change notification settings - Fork 76
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
10,948 additions
and
0 deletions.
There are no files selected for viewing
281 changes: 281 additions & 0 deletions
281
E005-testing-use-cases/sample-csharp/E005-testing-use-cases/Class1.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,281 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Text; | ||
using NUnit.Framework; | ||
|
||
namespace E005_testing_use_cases | ||
{ | ||
public class Program | ||
{ | ||
// first we refactor our factory from previous episode a little bit. | ||
// 1. rename TheFactoryJournal into -> Changes | ||
// 2. move state variables into a separate class (so that we can't accidentally touch it) | ||
// 3. allow loading this state variable from a journal | ||
// 4. add constructors and [Serializable] attribute to all events | ||
|
||
|
||
public static void Main() | ||
{ | ||
// you can run these tests from NUnit, but if you don't | ||
// have it, we'll run it in a console as well | ||
RunSpecification(new when_assign_employee_to_factory()); | ||
} | ||
|
||
static void RunSpecification(factory_specs specification) | ||
{ | ||
Console.WriteLine(new string('=', 80)); | ||
var cases = specification.GetType() | ||
.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); | ||
Console.WriteLine("Specification {0}", specification.GetType().Name); | ||
foreach (var methodInfo in cases) | ||
{ | ||
Console.WriteLine(new string('-', 80)); | ||
Console.WriteLine("Use case: {0}", methodInfo.Name.Replace("_"," ")); | ||
Console.WriteLine(); | ||
try | ||
{ | ||
specification.Setup(); | ||
methodInfo.Invoke(specification, null); | ||
Console.WriteLine("PASSED!"); | ||
} | ||
catch(Exception ex) | ||
{ | ||
Console.WriteLine("FAIL!"); | ||
} | ||
|
||
} | ||
|
||
|
||
} | ||
|
||
|
||
} | ||
|
||
|
||
public sealed class when_assign_employee_to_factory : factory_specs | ||
{ | ||
[Test] | ||
public void given_empty_factory() | ||
{ | ||
When = f => f.AssignEmployeeToFactory("fry"); | ||
Then = new List<IEvent> | ||
{ | ||
new EmployeeAssignedToFactory("fry") | ||
}; | ||
} | ||
[Test] | ||
public void given_bob_assigned_to_factory() | ||
{ | ||
Given = new IEvent[] | ||
{ | ||
new EmployeeAssignedToFactory("fry") | ||
}; | ||
When = f => f.AssignEmployeeToFactory("fry"); | ||
ThenException = ex => ex.Message.Contains("only one employee can have"); | ||
} | ||
[Test] | ||
public void given_empty_factory_and_bender() | ||
{ | ||
When = f => f.AssignEmployeeToFactory("bender"); | ||
ThenException = ex => ex.Message.Contains("Guys with name 'bender' are trouble"); | ||
} | ||
} | ||
|
||
|
||
public class FactoryAggregate | ||
{ | ||
// THE Factory Journal! | ||
public List<IEvent> Changes = new List<IEvent>(); | ||
FactoryState _state; | ||
|
||
public FactoryAggregate(FactoryState state) | ||
{ | ||
_state = state; | ||
} | ||
|
||
// internal "state" variables | ||
|
||
public void AssignEmployeeToFactory(string employeeName) | ||
{ | ||
//Print("?> Command: Assign employee {0} to factory", employeeName); | ||
|
||
if (_state.ListOfEmployeeNames.Contains(employeeName)) | ||
{ | ||
// yes, this is really weird check, but this factory has really strict rules. | ||
// manager should've remembered that | ||
Fail(":> the name of '{0}' only one employee can have", employeeName); | ||
|
||
return; | ||
} | ||
|
||
if (employeeName == "bender") | ||
{ | ||
Fail(":> Guys with name 'bender' are trouble."); | ||
return; | ||
} | ||
|
||
DoPaperWork("Assign employee to the factory"); | ||
RecordThat(new EmployeeAssignedToFactory(employeeName)); | ||
} | ||
|
||
void Fail(string message, params object[] args) | ||
{ | ||
throw new InvalidOperationException(string.Format(message, args)); | ||
} | ||
|
||
public void TransferShipmentToCargoBay(string shipmentName, CarPart[] parts) | ||
{ | ||
//Print("?> Command: transfer shipment to cargo bay"); | ||
if (_state.ListOfEmployeeNames.Count == 0) | ||
{ | ||
Fail(":> There has to be somebody at factory in order to accept shipment"); | ||
return; | ||
} | ||
|
||
if (_state.ShipmentsWaitingToBeUnloaded.Count > 2) | ||
{ | ||
Fail(":> More than two shipments can't fit into this cargo bay :("); | ||
return; | ||
} | ||
|
||
DoRealWork("opening cargo bay doors"); | ||
RecordThat(new ShipmentTransferredToCargoBay() | ||
{ | ||
ShipmentName = shipmentName, | ||
CarParts = parts | ||
}); | ||
|
||
var totalCountOfParts = parts.Sum(p => p.Quantity); | ||
if (totalCountOfParts > 10) | ||
{ | ||
RecordThat(new CurseWordUttered | ||
{ | ||
TheWord = "Boltov tebe v korobky peredach", | ||
Meaning = "awe in the face of the amount of parts delivered" | ||
}); | ||
} | ||
} | ||
|
||
|
||
void DoPaperWork(string workName) | ||
{ | ||
//Print(" > Work: papers... {0}... ", workName); | ||
|
||
} | ||
void DoRealWork(string workName) | ||
{ | ||
//Print(" > Work: heavy stuff... {0}...", workName); | ||
|
||
} | ||
void RecordThat(IEvent e) | ||
{ | ||
// we record by jotting down notes in our journal | ||
Changes.Add(e); | ||
// and also immediately change the state | ||
_state.Mutate(e); | ||
} | ||
|
||
|
||
} | ||
|
||
public class FactoryState | ||
{ | ||
public FactoryState(IEnumerable<IEvent> events) | ||
{ | ||
// this will load all events | ||
foreach (var @event in events) | ||
{ | ||
Mutate(@event); | ||
} | ||
} | ||
|
||
public readonly List<string> ListOfEmployeeNames = new List<string>(); | ||
public readonly List<CarPart[]> ShipmentsWaitingToBeUnloaded = new List<CarPart[]>(); | ||
|
||
// announcements inside the factory | ||
void AnnounceInsideFactory(EmployeeAssignedToFactory e) | ||
{ | ||
ListOfEmployeeNames.Add(e.EmployeeName); | ||
} | ||
void AnnounceInsideFactory(ShipmentTransferredToCargoBay e) | ||
{ | ||
ShipmentsWaitingToBeUnloaded.Add(e.CarParts); | ||
} | ||
void AnnounceInsideFactory(CurseWordUttered e) | ||
{ | ||
|
||
} | ||
public void Mutate(IEvent e) | ||
{ | ||
// we also announce this event inside factory. | ||
// so that all workers will immediately know | ||
// what is going inside. In essence we are telling compiler | ||
// to call one of the methods below | ||
((dynamic)this).AnnounceInsideFactory((dynamic)e); | ||
} | ||
} | ||
|
||
[Serializable] | ||
public class EmployeeAssignedToFactory : IEvent | ||
{ | ||
public string EmployeeName; | ||
|
||
public EmployeeAssignedToFactory(string employeeName) | ||
{ | ||
EmployeeName = employeeName; | ||
} | ||
|
||
public override string ToString() | ||
{ | ||
return string.Format("new worker joins our forces: '{0}'", EmployeeName); | ||
} | ||
} | ||
[Serializable] | ||
public class CurseWordUttered : IEvent | ||
{ | ||
public string TheWord; | ||
public string Meaning; | ||
|
||
public override string ToString() | ||
{ | ||
return string.Format("'{0}' was heard within the walls. It meant:\r\n '{1}'", TheWord, Meaning); | ||
} | ||
} | ||
[Serializable] | ||
public class ShipmentTransferredToCargoBay : IEvent | ||
{ | ||
public string ShipmentName; | ||
public CarPart[] CarParts; | ||
|
||
public override string ToString() | ||
{ | ||
var builder = new StringBuilder(); | ||
builder.AppendFormat("Shipment '{0}' transferred to cargo bay:", ShipmentName).AppendLine(); | ||
foreach (var carPart in CarParts) | ||
{ | ||
builder.AppendFormat(" {0} {1} pcs", carPart.Name, carPart.Quantity).AppendLine(); | ||
} | ||
return builder.ToString(); | ||
} | ||
} | ||
|
||
public interface IEvent | ||
{ | ||
|
||
} | ||
|
||
[Serializable] | ||
public sealed class CarPart | ||
{ | ||
public string Name; | ||
public int Quantity; | ||
public CarPart(string name, int quantity) | ||
{ | ||
Name = name; | ||
Quantity = quantity; | ||
} | ||
} | ||
|
||
} |
61 changes: 61 additions & 0 deletions
61
E005-testing-use-cases/sample-csharp/E005-testing-use-cases/E005-testing-use-cases.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
<PropertyGroup> | ||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | ||
<ProductVersion>8.0.30703</ProductVersion> | ||
<SchemaVersion>2.0</SchemaVersion> | ||
<ProjectGuid>{63386748-DD98-4C84-87B9-EB85631653BC}</ProjectGuid> | ||
<OutputType>Exe</OutputType> | ||
<AppDesignerFolder>Properties</AppDesignerFolder> | ||
<RootNamespace>E005_testing_use_cases</RootNamespace> | ||
<AssemblyName>E005-testing-use-cases</AssemblyName> | ||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> | ||
<FileAlignment>512</FileAlignment> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | ||
<DebugSymbols>true</DebugSymbols> | ||
<DebugType>full</DebugType> | ||
<Optimize>false</Optimize> | ||
<OutputPath>bin\Debug\</OutputPath> | ||
<DefineConstants>DEBUG;TRACE</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | ||
<DebugType>pdbonly</DebugType> | ||
<Optimize>true</Optimize> | ||
<OutputPath>bin\Release\</OutputPath> | ||
<DefineConstants>TRACE</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
</PropertyGroup> | ||
<PropertyGroup> | ||
<StartupObject /> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Reference Include="nunit.framework"> | ||
<HintPath>..\..\..\_misc\lib-dotnet\NUnit\nunit.framework.dll</HintPath> | ||
</Reference> | ||
<Reference Include="System" /> | ||
<Reference Include="System.Core" /> | ||
<Reference Include="System.Xml.Linq" /> | ||
<Reference Include="System.Data.DataSetExtensions" /> | ||
<Reference Include="Microsoft.CSharp" /> | ||
<Reference Include="System.Data" /> | ||
<Reference Include="System.Xml" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Compile Include="Class1.cs" /> | ||
<Compile Include="framework.cs" /> | ||
<Compile Include="Properties\AssemblyInfo.cs" /> | ||
</ItemGroup> | ||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | ||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. | ||
Other similar extension points exist, see Microsoft.Common.targets. | ||
<Target Name="BeforeBuild"> | ||
</Target> | ||
<Target Name="AfterBuild"> | ||
</Target> | ||
--> | ||
</Project> |
36 changes: 36 additions & 0 deletions
36
E005-testing-use-cases/sample-csharp/E005-testing-use-cases/Properties/AssemblyInfo.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using System.Reflection; | ||
using System.Runtime.CompilerServices; | ||
using System.Runtime.InteropServices; | ||
|
||
// General Information about an assembly is controlled through the following | ||
// set of attributes. Change these attribute values to modify the information | ||
// associated with an assembly. | ||
[assembly: AssemblyTitle("E005-testing-use-cases")] | ||
[assembly: AssemblyDescription("")] | ||
[assembly: AssemblyConfiguration("")] | ||
[assembly: AssemblyCompany("")] | ||
[assembly: AssemblyProduct("E005-testing-use-cases")] | ||
[assembly: AssemblyCopyright("Copyright © 2012")] | ||
[assembly: AssemblyTrademark("")] | ||
[assembly: AssemblyCulture("")] | ||
|
||
// Setting ComVisible to false makes the types in this assembly not visible | ||
// to COM components. If you need to access a type in this assembly from | ||
// COM, set the ComVisible attribute to true on that type. | ||
[assembly: ComVisible(false)] | ||
|
||
// The following GUID is for the ID of the typelib if this project is exposed to COM | ||
[assembly: Guid("01165e60-b7dd-4e6c-b4ff-9462478a2c46")] | ||
|
||
// Version information for an assembly consists of the following four values: | ||
// | ||
// Major Version | ||
// Minor Version | ||
// Build Number | ||
// Revision | ||
// | ||
// You can specify all the values or you can default the Build and Revision Numbers | ||
// by using the '*' as shown below: | ||
// [assembly: AssemblyVersion("1.0.*")] | ||
[assembly: AssemblyVersion("1.0.0.0")] | ||
[assembly: AssemblyFileVersion("1.0.0.0")] |
Oops, something went wrong.