Skip to content
Browse files

Using Deleporter to demonstrate cross-process mocking

  • Loading branch information...
1 parent f92784b commit bf2bdab066db43dc458a0d46ece7285912ddd0ad @SteveSanderson committed Mar 9, 2010
View
8 .gitignore
@@ -0,0 +1,8 @@
+*.suo
+*.csproj.user
+bin
+obj
+*.pdb
+_ReSharper*
+*.ReSharper.user
+desktop.ini
View
5 Guestbook.Spec/App.config
@@ -15,4 +15,7 @@
<appSettings>
<add key="RootUrl" value="http://localhost:8080/"/>
</appSettings>
-<startup><supportedRuntime version="v2.0.50727"/></startup></configuration>
+ <startup>
+ <supportedRuntime version="v2.0.50727"/>
+ </startup>
+</configuration>
View
BIN Guestbook.Spec/External Assemblies/Deleporter.dll
Binary file not shown.
View
15 Guestbook.Spec/Features/Browsing.feature
@@ -12,4 +12,17 @@ Scenario: Viewing existing entries
Then I should see a list of guestbook entries
And guestbook entries have an author
And guestbook entries have a posted date
- And guestbook entries have a comment
+ And guestbook entries have a comment
+
+Scenario: Most recent entries are displayed first
+ Given we have the following existing entries
+ | Name | Comment | Posted date |
+ | Mr. A | I like A | 2008-10-01 09:20 |
+ | Mrs. B | I like B | 2010-03-05 02:15 |
+ | Dr. C | I like C | 2010-02-20 12:21 |
+ And I am on the guestbook page
+ Then the guestbook entries includes the following, in this order
+ | Name | Comment | Posted date |
+ | Mrs. B | I like B | 2010-03-05 02:15 |
+ | Dr. C | I like C | 2010-02-20 12:21 |
+ | Mr. A | I like A | 2008-10-01 09:20 |
View
51 Guestbook.Spec/Features/Browsing.feature.cs
@@ -85,5 +85,56 @@ public virtual void ViewingExistingEntries()
#line hidden
testRunner.CollectScenarioErrors();
}
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Most recent entries are displayed first")]
+ public virtual void MostRecentEntriesAreDisplayedFirst()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Most recent entries are displayed first", ((string[])(null)));
+#line 17
+this.ScenarioSetup(scenarioInfo);
+#line hidden
+ TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] {
+ "Name",
+ "Comment",
+ "Posted date"});
+ table1.AddRow(new string[] {
+ "Mr. A",
+ "I like A",
+ "2008-10-01 09:20"});
+ table1.AddRow(new string[] {
+ "Mrs. B",
+ "I like B",
+ "2010-03-05 02:15"});
+ table1.AddRow(new string[] {
+ "Dr. C",
+ "I like C",
+ "2010-02-20 12:21"});
+#line 18
+ testRunner.Given("we have the following existing entries", ((string)(null)), table1);
+#line 23
+ testRunner.And("I am on the guestbook page");
+#line hidden
+ TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] {
+ "Name",
+ "Comment",
+ "Posted date"});
+ table2.AddRow(new string[] {
+ "Mrs. B",
+ "I like B",
+ "2010-03-05 02:15"});
+ table2.AddRow(new string[] {
+ "Dr. C",
+ "I like C",
+ "2010-02-20 12:21"});
+ table2.AddRow(new string[] {
+ "Mr. A",
+ "I like A",
+ "2008-10-01 09:20"});
+#line 24
+ testRunner.Then("the guestbook entries includes the following, in this order", ((string)(null)), table2);
+#line hidden
+ testRunner.CollectScenarioErrors();
+ }
}
}
View
9 Guestbook.Spec/Guestbook.Spec.csproj
@@ -32,6 +32,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
+ <Reference Include="Deleporter">
+ <HintPath>External Assemblies\Deleporter.dll</HintPath>
+ </Reference>
<Reference Include="Interop.SHDocVw">
<HintPath>External Assemblies\Interop.SHDocVw.dll</HintPath>
<EmbedInteropTypes>False</EmbedInteropTypes>
@@ -73,6 +76,9 @@
</Compile>
<Compile Include="PageWrappers\GuestbookEntriesList.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Steps\Infrastructure\SerializableTable.cs" />
+ <Compile Include="Steps\Infrastructure\TidyUp.cs" />
+ <Compile Include="Steps\MockData.cs" />
<Compile Include="Steps\ViewingGuestbook.cs" />
<Compile Include="Steps\Infrastructure\WebBrowser.cs" />
<Compile Include="Steps\Navigation.cs" />
@@ -89,9 +95,6 @@
</None>
</ItemGroup>
<ItemGroup>
- <WCFMetadata Include="Service References\" />
- </ItemGroup>
- <ItemGroup>
<ProjectReference Include="..\Guestbook.Domain\Guestbook.Domain.csproj">
<Project>{D7CDFBBF-7F13-44E1-8C56-D8884AFEDB5F}</Project>
<Name>Guestbook.Domain</Name>
View
27 Guestbook.Spec/Steps/Infrastructure/SerializableTable.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using TechTalk.SpecFlow;
+
+namespace Guestbook.Spec.Steps.Infrastructure
+{
+ /// <summary>
+ /// Just a way of sending a TechTalk.SpecFlow.Table's contents across a remoting boundary
+ /// </summary>
+ [Serializable]
+ public class SerializableTable
+ {
+ private readonly List<string> _header;
+ private readonly List<Dictionary<string, string>> _rows;
+
+ public SerializableTable(Table table)
+ {
+ _header = table.Header.ToList();
+ _rows = (from row in table.Rows
+ select _header.ToDictionary(key => key, key => row[key])).ToList();
+ }
+
+ public IList<string> Header { get { return _header.AsReadOnly(); } }
+ public IList<Dictionary<string, string>> Rows { get { return _rows.Select(x => new Dictionary<string, string>(x)).ToList(); } }
+ }
+}
View
74 Guestbook.Spec/Steps/Infrastructure/TidyUp.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.Remoting.Messaging;
+using DeleporterCore.Client;
+using TechTalk.SpecFlow;
+
+namespace Guestbook.Spec.Steps.Infrastructure
+{
+ /// <summary>
+ /// Store the actions in a dictionary keyed by a CallContext-specific Guid
+ /// The test appdomain will set a new guid at the start of each scenario
+ /// </summary>
+ [Binding]
+ public static class TidyUp
+ {
+ private const string CallContextKey = "__tidyUp_scenarioId";
+ private static readonly Dictionary<Guid, IList<Action>> TaskStores = new Dictionary<Guid, IList<Action>>();
+ private static Guid ScenarioGuid
+ {
+ get { return (Guid) CallContext.LogicalGetData(CallContextKey); }
+ set { CallContext.LogicalSetData(CallContextKey, value);}
+ }
+
+ public static IEnumerable<Action> Tasks
+ {
+ get {
+ if (!TaskStores.ContainsKey(ScenarioGuid))
+ TaskStores[ScenarioGuid] = new List<Action>();
+ return TaskStores[ScenarioGuid];
+ }
+ }
+
+ public static void AddTask(Action task)
+ {
+ ((List<Action>) Tasks).Add(task);
+ }
+
+ [BeforeScenario]
+ public static void Prep()
+ {
+ ScenarioGuid = Guid.NewGuid();
+ AddTask(() => Deleporter.Run(PerformTidyUp));
+ }
+
+ [AfterScenario]
+ public static void PerformTidyUp()
+ {
+ try {
+ var exceptions = new List<Exception>();
+ foreach (var task in Tasks) {
+ try { task(); }
+ catch (Exception ex) { exceptions.Add(ex); }
+ }
+ if (exceptions.Count == 1)
+ throw exceptions[0];
+ else if (exceptions.Count > 1)
+ throw new MultiException(exceptions);
+ }
+ finally {
+ TaskStores.Remove(ScenarioGuid);
+ }
+ }
+
+ public class MultiException : Exception
+ {
+ protected IEnumerable<Exception> InnerExceptions { get; private set; }
+
+ public MultiException(IEnumerable<Exception> exceptions)
+ {
+ InnerExceptions = exceptions;
+ }
+ }
+ }
+}
View
39 Guestbook.Spec/Steps/MockData.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Linq;
+using DeleporterCore.Client;
+using Guestbook.Domain.Entities;
+using Guestbook.Domain.Repositories;
+using Guestbook.Spec.Steps.Infrastructure;
+using Moq;
+using TechTalk.SpecFlow;
+
+namespace Guestbook.Spec.Steps
+{
+ [Binding]
+ public class MockData
+ {
+ [Given(@"we have the following existing entries")]
+ public void GivenWeHaveTheFollowingExistingEntries(Table table)
+ {
+ var tableSerialized = new SerializableTable(table);
+
+ Deleporter.Run(() =>
+ {
+ var originalRepository = IoC.CurrentGuestbookEntryRepository;
+ TidyUp.AddTask(() => { IoC.CurrentGuestbookEntryRepository = originalRepository; });
+
+ var mockRepository = new Mock<IGuestbookEntryRepository>();
+ mockRepository.Setup(x => x.Entries)
+ .Returns((from row in tableSerialized.Rows
+ select new GuestbookEntry
+ {
+ Author = row["Name"],
+ Comment = row["Comment"],
+ PostedDate = Convert.ToDateTime(row["Posted date"])
+ }).AsQueryable());
+
+ IoC.CurrentGuestbookEntryRepository = mockRepository.Object;
+ });
+ }
+ }
+}
View
25 Guestbook.Spec/Steps/ViewingGuestbook.cs
@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using Guestbook.Spec.PageWrappers;
-using Guestbook.Spec.Steps.Infrastructure;
using NUnit.Framework;
using TechTalk.SpecFlow;
@@ -30,17 +29,23 @@ public void ThenGuestbookEntriesHaveA(string propertyName)
}
}
- [Then(@"the guestbook entries includes the following")]
- public void ThenTheGuestbookEntriesIncludesTheFollowing(Table table)
+ [Then(@"the guestbook entries includes the following(, in this order)?")]
+ public void ThenTheGuestbookEntriesIncludesTheFollowing(string inThisOrder, Table table)
{
- var entries = GuestbookEntriesList.DisplayedEntries.ToList();
+ bool requiresExactOrder = !string.IsNullOrEmpty(inThisOrder);
+ List<GuestbookEntriesList.Entry> entries = GuestbookEntriesList.DisplayedEntries.ToList();
+
+ Func<GuestbookEntriesList.Entry, TableRow, bool> equalityComparer = (entry, row) => {
+ return (row["Name"] == entry.Author
+ && row["Comment"] == entry.Comment
+ && IsValidPostedDate(entry, row["Posted date"]));
+ };
+
+ int lastMatchIndex = -1;
foreach (var row in table.Rows) {
- var matchingEntries = from entry in entries
- where entry.Author == row["Name"]
- && entry.Comment == row["Comment"]
- && IsValidPostedDate(entry, row["Posted date"])
- select entry;
- CollectionAssert.IsNotEmpty(matchingEntries);
+ lastMatchIndex = entries.FindIndex(requiresExactOrder ? lastMatchIndex + 1 : 0, entry => equalityComparer(entry, row));
+ if (lastMatchIndex < 0)
+ Assert.Fail("No match for " + row);
}
}
View
2 Guestbook/Controllers/GuestbookController.cs
@@ -14,7 +14,7 @@ public class GuestbookController : Controller
public ActionResult Index()
{
- return View(_repository.Entries);
+ return View(_repository.Entries.OrderByDescending(x => x.PostedDate));
}
public ActionResult Post()
View
3 Guestbook/Guestbook.csproj
@@ -33,6 +33,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
+ <Reference Include="Deleporter">
+ <HintPath>..\Guestbook.Spec\External Assemblies\Deleporter.dll</HintPath>
+ </Reference>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
View
6 Guestbook/Web.config
@@ -47,6 +47,9 @@
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+
+ <!-- If you're using Visual Studio's built-in web server, or IIS 5 or 6 -->
+ <add name="DeleporterServerModule" type="DeleporterCore.Server.DeleporterServerModule, Deleporter" />
</httpModules>
<httpHandlers>
<remove verb="*" path="*.asmx"/>
@@ -62,6 +65,9 @@
<remove name="ScriptModule"/>
<add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="UrlRoutingModule" preCondition="" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+
+ <!-- If you're using IIS 7+ -->
+ <add name="DeleporterServerModule" type="DeleporterCore.Server.DeleporterServerModule, Deleporter" />
</modules>
<handlers>
<remove name="UrlRoutingHandler"/>

0 comments on commit bf2bdab

Please sign in to comment.
Something went wrong with that request. Please try again.