Skip to content

Instantly share code, notes, and snippets.

@MarkNijhof
Created August 3, 2010 21:01
Show Gist options
  • Save MarkNijhof/507143 to your computer and use it in GitHub Desktop.
Save MarkNijhof/507143 to your computer and use it in GitHub Desktop.
namespace Test.Fohjin.DDD
{
[Specification]
public abstract class BaseTestFixture
{
protected Exception CaughtException;
protected virtual void Given() { }
protected abstract void When();
protected virtual void Finally() { }
[Given]
public void Setup()
{
CaughtException = new ThereWasNoExceptionButOneWasExpectedException();
Given();
try
{
When();
}
catch (Exception exception)
{
CaughtException = exception;
}
finally
{
Finally();
}
}
}
public class GivenAttribute : SetUpAttribute { }
public class ThenAttribute : TestAttribute { }
public class SpecificationAttribute : TestFixtureAttribute { }
}
namespace Test.Fohjin.DDD
{
public class Very_adding_a_number_to_the_subject_under_test : BaseTestFixture
{
private int SubjectUnderTest;
protected override void Given()
{
SubjectUnderTest = 2;
}
protected override void When()
{
SubjectUnderTest += 2;
}
[Then]
public void Then_the_new_value_of_the_subject_under_test_will_be_4()
{
SubjectUnderTest.WillBe(4);
}
}
}
namespace Test.Fohjin.DDD
{
[Specification]
public abstract class BaseTestFixture<TSubjectUnderTest>
{
private Dictionary<Type, object> mocks;
protected Dictionary<Type, object> DoNotMock;
protected TSubjectUnderTest SubjectUnderTest;
protected Exception CaughtException;
protected virtual void SetupDependencies() { }
protected virtual void Given() { }
protected abstract void When();
protected virtual void Finally() { }
[Given]
public void Setup()
{
mocks = new Dictionary<Type, object>();
DoNotMock = new Dictionary<Type, object>();
CaughtException = new ThereWasNoExceptionButOneWasExpectedException();
BuildMocks();
SetupDependencies();
SubjectUnderTest = BuildSubjectUnderTest();
Given();
try
{
When();
}
catch (Exception exception)
{
CaughtException = exception;
}
finally
{
Finally();
}
}
public Mock<TType> OnDependency<TType>() where TType : class
{
return (Mock<TType>)mocks[typeof(TType)];
}
private TSubjectUnderTest BuildSubjectUnderTest()
{
var constructorInfo = typeof(TSubjectUnderTest).GetConstructors().First();
var parameters = new List<object>();
foreach (var mock in mocks)
{
object theObject;
if (!DoNotMock.TryGetValue(mock.Key, out theObject))
theObject = ((Mock) mock.Value).Object;
parameters.Add(theObject);
}
return (TSubjectUnderTest)constructorInfo.Invoke(parameters.ToArray());
}
private void BuildMocks()
{
var constructorInfo = typeof(TSubjectUnderTest).GetConstructors().First();
foreach (var parameter in constructorInfo.GetParameters())
{
mocks.Add(parameter.ParameterType, CreateMock(parameter.ParameterType));
}
}
private static object CreateMock(Type type)
{
var constructorInfo = typeof(Mock<>).MakeGenericType(type).GetConstructors().First();
return constructorInfo.Invoke(new object[] { });
}
}
}
namespace Test.Fohjin.DDD.Scenarios.Receiving_money_transfer
{
public class When_receiving_a_money_transfer : BaseTestFixture<MoneyReceiveService>
{
protected override void SetupDependencies()
{
OnDependency<IReportingRepository>()
.Setup(x => x.GetByExample<AccountReport>(It.IsAny<object>()))
.Returns(new List<AccountReport> { new AccountReport(Guid.NewGuid(), Guid.NewGuid(), "AccountName", "target account number") });
}
protected override void When()
{
SubjectUnderTest.Receive(new MoneyTransfer("source account number", "target account number", 123.45M));
}
[Then]
public void Then_the_newly_created_account_will_be_saved()
{
OnDependency<IBus>().Verify(x => x.Publish(It.IsAny<ReceiveMoneyTransferCommand>()));
}
}
}
namespace Test.Fohjin.DDD.Scenarios.Adding_a_new_client
{
public class When_in_the_GUI_the_phone_number_of_the_new_client_is_saved : PresenterTestFixture<ClientDetailsPresenter>
{
private object CreateClientCommand;
protected override void SetupDependencies()
{
OnDependency<IPopupPresenter>()
.Setup(x => x.CatchPossibleException(It.IsAny<Action>()))
.Callback<Action>(x => x());
OnDependency<IBus>()
.Setup(x => x.Publish(It.IsAny<object>()))
.Callback<object>(x => CreateClientCommand = x);
}
protected override void Given()
{
Presenter.SetClient(null);
Presenter.Display();
On<IClientDetailsView>().ValueFor(x => x.ClientName).IsSetTo("New Client Name");
On<IClientDetailsView>().FireEvent(x => x.OnFormElementGotChanged += null);
On<IClientDetailsView>().FireEvent(x => x.OnSaveNewClientName += null);
On<IClientDetailsView>().ValueFor(x => x.Street).IsSetTo("Street");
On<IClientDetailsView>().ValueFor(x => x.StreetNumber).IsSetTo("123");
On<IClientDetailsView>().ValueFor(x => x.PostalCode).IsSetTo("5000");
On<IClientDetailsView>().ValueFor(x => x.City).IsSetTo("Bergen");
On<IClientDetailsView>().FireEvent(x => x.OnFormElementGotChanged += null);
On<IClientDetailsView>().FireEvent(x => x.OnSaveNewAddress += null);
On<IClientDetailsView>().ValueFor(x => x.PhoneNumber).IsSetTo("1234567890");
On<IClientDetailsView>().FireEvent(x => x.OnFormElementGotChanged += null);
}
protected override void When()
{
On<IClientDetailsView>().FireEvent(x => x.OnSaveNewPhoneNumber += null);
}
[Then]
public void Then_the_save_button_will_be_disabled()
{
On<IClientDetailsView>().VerifyThat.Method(x => x.DisableSaveButton()).WasCalled();
}
[Then]
public void Then_a_create_client_command_with_all_collected_information_will_be_published()
{
On<IBus>().VerifyThat.Method(x => x.Publish(It.IsAny<CreateClientCommand>())).WasCalled();
CreateClientCommand.As<CreateClientCommand>().ClientName.WillBe("New Client Name");
CreateClientCommand.As<CreateClientCommand>().Street.WillBe("Street");
CreateClientCommand.As<CreateClientCommand>().StreetNumber.WillBe("123");
CreateClientCommand.As<CreateClientCommand>().PostalCode.WillBe("5000");
CreateClientCommand.As<CreateClientCommand>().City.WillBe("Bergen");
CreateClientCommand.As<CreateClientCommand>().PhoneNumber.WillBe("1234567890");
}
[Then]
public void Then_overview_panel_will_be_shown()
{
On<IClientDetailsView>().VerifyThat.Method(x => x.Close()).WasCalled();
}
}
}
namespace Test.Fohjin.DDD
{
public class MockDsl<TType> where TType : class
{
private readonly IDictionary<Type, object> _mocks;
public MockDsl(IDictionary<Type, object> mocks)
{
_mocks = mocks;
}
public ValueSetter<TType, TProperty> ValueFor<TProperty>(Expression<Func<TType, TProperty>> selector)
{
return new ValueSetter<TType, TProperty>(_mocks, selector);
}
public void FireEvent(Action<TType> fieldSelector)
{
if (!_mocks.ContainsKey(typeof(TType)))
throw new Exception(string.Format("The requested dependency '{0}' is not specified in the constructor", typeof(TType).FullName));
var mock = (Mock<TType>)_mocks[typeof(TType)];
mock.Raise(fieldSelector);
}
public Verifier<TType> VerifyThat { get { return new Verifier<TType>(_mocks); } }
}
public class Verifier<TType> where TType : class
{
private readonly IDictionary<Type, object> _mocks;
public Verifier(IDictionary<Type, object> mocks)
{
_mocks = mocks;
}
public void ValueIsSetFor(Action<TType> selector)
{
if (!_mocks.ContainsKey(typeof(TType)))
throw new Exception(string.Format("The requested dependency '{0}' is not specified in the constructor", typeof(TType).FullName));
var mock = (Mock<TType>)_mocks[typeof(TType)];
mock.VerifySet(selector);
}
public MethodVerifier<TType> Method(Expression<Action<TType>> selector)
{
return new MethodVerifier<TType>(_mocks, selector);
}
}
public class MethodVerifier<TType> where TType : class
{
private readonly IDictionary<Type, object> _mocks;
private readonly Expression<Action<TType>> _fieldSelector;
public MethodVerifier(IDictionary<Type, object> mocks, Expression<Action<TType>> fieldSelector)
{
_mocks = mocks;
_fieldSelector = fieldSelector;
}
public void WasCalled()
{
if (!_mocks.ContainsKey(typeof(TType)))
throw new Exception(string.Format("The requested dependency '{0}' is not specified in the constructor", typeof(TType).FullName));
var mock = (Mock<TType>)_mocks[typeof(TType)];
mock.Verify(_fieldSelector);
}
}
public class ValueSetter<TType, TProperty> where TType : class
{
private readonly IDictionary<Type, object> _mocks;
private readonly Expression<Func<TType, TProperty>> _fieldSelector;
public ValueSetter(IDictionary<Type, object> mocks, Expression<Func<TType, TProperty>> fieldSelector)
{
_mocks = mocks;
_fieldSelector = fieldSelector;
}
public void IsSetTo(TProperty value)
{
if (!_mocks.ContainsKey(typeof(TType)))
throw new Exception(string.Format("The requested dependency '{0}' is not specified in the constructor", typeof(TType).FullName));
var mock = (Mock<TType>)_mocks[typeof(TType)];
mock.SetupGet(_fieldSelector).Returns(value);
}
}
}
namespace Test.Fohjin.DDD
{
[Specification]
public abstract class CommandTestFixture<TCommand, TCommandHandler, TAggregateRoot>
where TCommand : class, ICommand
where TCommandHandler : class, ICommandHandler<TCommand>
where TAggregateRoot : class, IOrginator, IEventProvider<IDomainEvent>, new()
{
private IDictionary<Type, object> mocks;
protected TAggregateRoot AggregateRoot;
protected ICommandHandler<TCommand> CommandHandler;
protected Exception CaughtException;
protected IEnumerable<IDomainEvent> PublishedEvents;
protected virtual void SetupDependencies() { }
protected virtual IEnumerable<IDomainEvent> Given()
{
return new List<IDomainEvent>();
}
protected virtual void Finally() { }
protected abstract TCommand When();
[Given]
public void Setup()
{
mocks = new Dictionary<Type, object>();
CaughtException = new ThereWasNoExceptionButOneWasExpectedException();
AggregateRoot = new TAggregateRoot();
AggregateRoot.LoadFromHistory(Given());
CommandHandler = BuildCommandHandler();
SetupDependencies();
try
{
CommandHandler.Execute(When());
PublishedEvents = AggregateRoot.GetChanges();
}
catch (Exception exception)
{
CaughtException = exception;
}
finally
{
Finally();
}
}
public Mock<TType> OnDependency<TType>() where TType : class
{
return (Mock<TType>)mocks[typeof(TType)];
}
private ICommandHandler<TCommand> BuildCommandHandler()
{
var constructorInfo = typeof(TCommandHandler).GetConstructors().First();
foreach (var parameter in constructorInfo.GetParameters())
{
if (parameter.ParameterType == typeof(IDomainRepository<IDomainEvent>))
{
var repositoryMock = new Mock<IDomainRepository<IDomainEvent>>();
repositoryMock.Setup(x => x.GetById<TAggregateRoot>(It.IsAny<Guid>())).Returns(AggregateRoot);
repositoryMock.Setup(x => x.Add(It.IsAny<TAggregateRoot>())).Callback<TAggregateRoot>(x => AggregateRoot = x);
mocks.Add(parameter.ParameterType, repositoryMock);
continue;
}
mocks.Add(parameter.ParameterType, CreateMock(parameter.ParameterType));
}
return (ICommandHandler<TCommand>)constructorInfo.Invoke(mocks.Values.Select(x => ((Mock) x).Object).ToArray());
}
private static object CreateMock(Type type)
{
var constructorInfo = typeof (Mock<>).MakeGenericType(type).GetConstructors().First();
return constructorInfo.Invoke(new object[]{});
}
}
public class ThereWasNoExceptionButOneWasExpectedException : Exception {}
}
namespace Test.Fohjin.DDD.Scenarios.Withdrawing_cash
{
public class When_withdrawing_cash : CommandTestFixture<WithdrawlCashCommand, WithdrawlCashCommandHandler, ActiveAccount>
{
protected override IEnumerable<IDomainEvent> Given()
{
yield return new AccountOpenedEvent(Guid.NewGuid(), Guid.NewGuid(), "AccountName", "1234567890");
yield return new CashDepositedEvent(20, 20);
}
protected override WithdrawlCashCommand When()
{
return new WithdrawlCashCommand(Guid.NewGuid(), 5);
}
[Then]
public void Then_a_cash_withdrawn_event_will_be_published()
{
PublishedEvents.Last().WillBeOfType<CashWithdrawnEvent>();
}
[Then]
public void Then_the_published_event_will_contain_the_amount_and_new_account_balance()
{
PublishedEvents.Last<CashWithdrawnEvent>().Balance.WillBe(15);
PublishedEvents.Last<CashWithdrawnEvent>().Amount.WillBe(5);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment