Getting Issue with Source Cache in ReactiveUI - c#

I have one issue in subscribing to source cache. Let me describe the problem.
Lets say I have Test Class
public class Test {
public bool feature1 {get; set;} = false;
public bool feature2 {get; set; } = false;
public string name;
public Test(string name){
this.name = name
}
}
I want to see the changes happening in the property of test class and subscriber react according to change. But with current Implementation getting notification only when source is getting updated with new data not if any property of element in source cache is getting updated.
class Notifier {
public SourceCache<Test, string> testClassNotifier = new SourceCache<Test, string>(x => x.Name);
public Notifier(){
Task.Run(() =>
{
this.AddOrUpdateSourceCache();
this.SubscribeTestObj1();
this.SubscribeTestObj2();
}).ConfigureAwait(false);
}
private AddOrUpdateSourceCache()
{
List<Test> testListObj = new List<Test>() { new Test("test1"), new Test("test2") };
for (Test obj : testListObj) {
this.testClassNotifier.AddOrUpdate(obj);
}
Task.Run(async () => {
for(int i = 0; i<2; i++) {
this.testListObj[i].feature1 = true;
await Task.Delay(4000).ConfigureAwait(false);
// I want here to my get the notification in change with intial values as well.
}
}).ConfiguareAwait(false);
}
private IObservable<Test,string> GetNotification(string name){
// which api should use here ?? Or any way I can use `WhenAny` here.
return this.testClassNotifier.Watch(name);
}
private SubscribeTestObj1() {
this.GetNotification("test1").Subscribe(obj => // do something);
}
private SubscribeTestObj1() {
this.GetNotification("test2").Subscribe(obj => // do something);
}
}

One solution: implement INotifyPropertyChanged on the Test class, and use AutoRefresh()
Example:
public class Test : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
bool _feature1 = false;
public bool feature1
{
get => _feature1;
set
{
_feature1 = value;
PropertyChanged?.Invoke(this, new(nameof(feature1)));
}
}
// ... see the rest of the class in OP's question
}
Test:
var source = new SourceCache<Test, string>(x => x.name);
var a = new Test("a");
var b = new Test("b");
source
.Connect()
.AutoRefresh()
.Watch(b.name)
.Subscribe(change => Console.WriteLine($"Reason: <{change.Reason}> feature1: <{change.Current.feature1}>"));
source.AddOrUpdate(a);
source.AddOrUpdate(b);
b.feature1 = true;
Output:
Reason: <Add> feature1: <False>
Reason: <Refresh> feature1: <True>

Related

How to Schedule in the Initial state of MassTransit saga

I created a state machine saga that will receive multiple messages and only after a given time period elapses, I want it to continue its work. I figured the only way to do it with mass transit is to go with the scheduling capabilities of the framework.
The saga code (shortened for brevity) is given below:
public class CheckFeedSubmissionStateMachine : MassTransitStateMachine<CheckFeedSubmissionState>
{
public State? WaitingForTimeoutExpiration { get; private set; }
public State? FetchingSubmissionData { get; private set; }
public Event<CheckFeedSubmissionCommand> CheckFeedSubmissionCommandReceived { get; private set; }
public Event<FeedSubmissionListReceivedEvent> FeedSubmissionListReceived { get; private set; }
public Event<FeedSubmissionListErrorReceivedEvent> FeedSubmissionListErrorReceived { get; private set; }
public Event<FeedSubmissionResultReceivedEvent> FeedSubmissionResultReceived { get; private set; }
public Event<FeedSubmissionResultErrorReceivedEvent> FeedSubmissionResultErrorReceived { get; private set; }
public Schedule<CheckFeedSubmissionState, SchedulingCompletionTimeoutExpired> ScheduleCompletionTimeout { get; private set; }
private readonly int _scheduleDelay;
public CheckFeedSubmissionStateMachine(IOptions<SagasOptions> options)
{
_scheduleDelay = int.Parse(options.Value.CheckFeedSubmissionStateMachine["ScheduleDelay"]);
Configure();
BuildProcess();
}
private void Configure()
{
Event(
() => CheckFeedSubmissionCommandReceived,
e => e.CorrelateById(x => x.Message.PartnerGuid));
Schedule(() => ScheduleCompletionTimeout, instance => instance.SchedulingCompletionTimeoutTokenId, s =>
{
s.Delay = TimeSpan.FromSeconds(_scheduleDelay);
s.Received = r => r.CorrelateById(context => context.Message.CorrelationId);
});
InstanceState(state => state.CurrentState);
}
private void BuildProcess()
{
Initially(
When(CheckFeedSubmissionCommandReceived)
.Then(InitializeState)
.Then(StoreSubmissionIds)
.Schedule(ScheduleCompletionTimeout, ScheduleEvent)
.TransitionTo(WaitingForTimeoutExpiration));
During(WaitingForTimeoutExpiration,
When(CheckFeedSubmissionCommandReceived)
.Then(StoreSubmissionIds),
When(ScheduleCompletionTimeout.Received)
.Activity(QueueGetFeedSubmissionListRequest)
.TransitionTo(FetchingSubmissionData));
// the rest ommited for brevity
}
private void InitializeState(BehaviorContext<CheckFeedSubmissionState, CheckFeedSubmissionCommand> ctx) =>
ctx.Instance.PartnerId = ctx.Data.PartnerId;
private void StoreSubmissionIds(BehaviorContext<CheckFeedSubmissionState, CheckFeedSubmissionCommand> ctx)
{
ctx.Instance.SubmissionIdToStatusMap[ctx.Data.FeedSubmissionId] = FeedProcessingStatus.Submitted;
ctx.Instance.SubmissionIdsToCorrelationIdsMap[ctx.Data.FeedSubmissionId] = ctx.Data.CorrelationId;
}
private Task<SchedulingCompletionTimeoutExpired> ScheduleEvent<TEvent>(
ConsumeEventContext<CheckFeedSubmissionState, TEvent> ctx) where TEvent : class =>
ctx.Init<SchedulingCompletionTimeoutExpired>(new { ctx.Instance.CorrelationId });
private EventActivityBinder<CheckFeedSubmissionState, SchedulingCompletionTimeoutExpired> QueueGetFeedSubmissionListRequest(
IStateMachineActivitySelector<CheckFeedSubmissionState, SchedulingCompletionTimeoutExpired> sel) =>
sel.OfType<QueueGetFeedSubmissionListActivity>();
}
The one test that I created for it aims at checking if both published messages have been preserved in the saga, the code below:
[Fact]
public async Task GivenCheckFeedSubmissionCommand_WhenAnotherCheckFeedSubmissionCommandIsReceived_ThenTheSagaStoresBothSubmissionIds()
{
var (harness, sagaHarness) = GetTestComponents();
var partnerGuid = Guid.NewGuid();
await harness.Start();
try
{
await harness.Bus.Publish(GetInitiatingEvent("1", partnerGuid));
await Consumption<CheckFeedSubmissionCommand>(harness, sagaHarness, 1);
await harness.Bus.Publish(GetInitiatingEvent("2", partnerGuid));
await Consumption<CheckFeedSubmissionCommand>(harness, sagaHarness, 2);
var state = sagaHarness.Sagas.Contains(partnerGuid);
state.CurrentState.Should().Be("WaitingForTimeoutExpiration");
state.SubmissionIdsToCorrelationIdsMap.Should().ContainKeys("1", "2");
}
finally
{
await harness.Stop();
}
}
private static (InMemoryTestHarness, IStateMachineSagaTestHarness<CheckFeedSubmissionState, CheckFeedSubmissionStateMachine>) GetTestComponents() =>
TestHarnessFactory.Create<CheckFeedSubmissionState, CheckFeedSubmissionStateMachine>(
sp => sp
.AddSingleton(Options.Create(new SagasOptions
{
CheckFeedSubmissionStateMachine = new Dictionary<string, string>
{
["ScheduleDelay"] = "0"
}
})));
private static CheckFeedSubmissionCommand GetInitiatingEvent(string feedSubmissionId, Guid partnerGuid) =>
new(Guid.NewGuid(), "1", partnerGuid, feedSubmissionId);
private static async Task Consumption<TEvent>(
InMemoryTestHarness harness,
IStateMachineSagaTestHarness<CheckFeedSubmissionState, CheckFeedSubmissionStateMachine> sagaHarness,
int expectedCount)
where TEvent : class
{
if (expectedCount == 1)
{
var harnessConsumed = harness.Consumed.SelectAsync<TEvent>().Any();
var sagaConsumed = sagaHarness.Consumed.SelectAsync<TEvent>().Any();
await Task.WhenAll(harnessConsumed, sagaConsumed);
}
else
{
int harnessConsumedCount;
int sagaConsumedCount;
do
{
var harnessConsumedTask = harness.Consumed.SelectAsync<TEvent>().Count();
var sagaConsumedTask = sagaHarness.Consumed.SelectAsync<TEvent>().Count();
harnessConsumedCount = await harnessConsumedTask;
sagaConsumedCount = await sagaConsumedTask;
await Task.Delay(1000);
} while (harnessConsumedCount < expectedCount && sagaConsumedCount < expectedCount);
}
}
The problem is that when I invoke this line .Schedule(ScheduleCompletionTimeout, ScheduleEvent) in the Initially/When phase, it somehow interferes with state switching and the saga does not switch to the next state - it stays in the Initial state indefinitely. I confirmed it both by inspecting the state variable in the test and by setting a breakpoint in the InitializeState method - it gets hit twice. When I remove that line doing the scheduling, the test passes, though I can't do that, because I need it. Any help?
It's likely you don't have a scheduler configured for the bus with the test harness. If you had logging enabled for the test, you'd see the error in the logs.
The bus configuration for the test harness should let you add the scheduler:
configurator.UseDelayedMessageScheduler();
There is a configuration event on the test harness, OnConfigureInMemoryBus or something like that, which you can use to configure the bus.

Detect when property changed at any element in the list

I googled a lot and still not get an answer. The problem is very silly - I have collection of elements, and need to know when any property changed on any element. Not collection itself changed.
Pseudocode:
public class Item : ReactiveObject {
[ObservableAsProperty]
public bool PropertyToMonitor { get; }
}
public ReadOnlyObservableCollection<Item> Items;
So, how to get that any item in the Items list got PropertyToMonitor updated in observable manner?
Ugly workaround I use by now is:
Observable.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1))
.Subscribe(_ => InvokeAsync(StateHasChanged))
Spend a day and gave up after all.
UPD: resolved with quite straight solution, pseudocode:
public class UnitTest3 : ReactiveObject
{
private readonly ITestOutputHelper _output;
public class Item : ReactiveObject
{
[Reactive]
public bool PropertyToMonitor { get; set; }
}
private ObservableCollection<Item> Items = new ObservableCollection<Item>();
public UnitTest3(ITestOutputHelper output)
{
this._output = output;
}
[Fact]
public void Test1()
{
bool signalled = false;
var item = new Item { };
Items.Add(new[] {item});
Items.Select(s => s.WhenPropertyChanged(p => p.PropertyToMonitor).Select(v => v.Value))
.Merge()
.Subscribe(v =>
{
_output.WriteLine("signalled: {0}", v);
signalled = v;
});
item.PropertyToMonitor = true;
signalled.Should().BeTrue();
}
}

Mock custom implementation of CodeAccessSecurityAttribute

I have a custom implementation of CodeAccessSecurityAttribute that is connecting external sources to do a validation.
[Serializable]
[AttributeUsage(AttributeTargets.Method)]
public class IsAuthorizedAttribute : CodeAccessSecurityAttribute
{
private static readonly PrincipalPermission Allowed = new PrincipalPermission(PermissionState.None);
private static readonly PrincipalPermission NotAllowed = new PrincipalPermission(PermissionState.Unrestricted);
public string EntityObject { get; set; }
public string Field { get; set; }
public char Expected { get; set; }
public IsAuthorizedAttribute(SecurityAction action)
: base(action)
{
//setup
}
public override IPermission CreatePermission()
{
return IsAuthorised(EntityObject, Field, Expected, ServicesConfiguration) ? Allowed : NotAllowed;
}
private static bool IsAuthorised(string entityObject, string field, char expected, ServicesConfiguration servicesConfiguration)
{
bool? response = null;
//check external stuff
return response ?? false;
}
}
I have decorated my methods with this attribute:
[IsAuthorized(SecurityAction.Demand, EntityObject = Fields.UserManagement, Field = Fields.AllowDisplay, Expected = '1')]
public List<Group> GetUserGroups()
{
var response = new List<Group>();
//Get the groups from the database
var groups = groupManager.FindAll();
//Map them to the output group type
response = groups.Select(x => new Group()
{
ID = x.ID,
Name = x.Name,
Alias = x.Alias,
Description = x.Description
}).ToList();
return response;
}
I now want to unit test this method, but the attribute is fired. I have tried some things to mock the attribute, but without success.
I'm using Moq and Smocks.
This is my unit test without a mocked instance of the attribute:
[TestMethod]
public void GetUserGroups_UserGroupsFound_UserGroupsReturned()
{
Smock.Run(context =>
{
//Arrange
Setup();
m_Container
.RegisterMock<IGroupManager>()
.Setup(x => x.FindAllFromCache())
.Returns(new List<Concept.Security.MasterData.Domain.Group>()
{
new Concept.Security.MasterData.Domain.Group()
{
Name = "MyUserGroup",
Alias = "My User Group",
Description = "My user group description",
System = false,
Authorizations = "000001111100000000"
},
new Concept.Security.MasterData.Domain.Group()
{
Name = "MySecondUserGroup",
Alias = "My Second User Group",
Description = "My second user group description",
System = false,
Authorizations = "000000000000000000"
}
});
var identityService = new UserManagementService(m_Container, m_UserAuthorizationManager.Object, m_IdentityService.Object);
//** begin add mocked attribute **//
//** end add mocked attribute **//
//Act
var response = identityService.GetUserGroups();
//Assert
Assert.AreEqual(2, response.Count);
Assert.AreEqual(1, response.Where(x => x.Alias == "MyUserGroup").Count());
Assert.AreEqual(1, response.Where(x => x.Alias == "MySecondUserGroup").Count());
Assert.AreEqual(2, response.Where(x => x.Authorizations == null).Count());
});
}
Running this results in an exception because the attribute tries to connect the external services and they aren't (and can't be) setup to receive requests.
So, I try to add a mocked attribute:
//** begin add mocked attribute **//
var identityService = new UserManagementService(m_Container, m_UserAuthorizationManager.Object, m_IdentityService.Object);
var IsAuthorizedAttribute = new Mock<IsAuthorizedAttribute>(MockBehavior.Strict, new object[] { SecurityAction.Demand });
IsAuthorizedAttribute.Setup(x => x.CreatePermission()).Returns(new PrincipalPermission(PermissionState.None));
TypeDescriptor.AddAttributes(identityService, IsAuthorizedAttribute.Object);
//** end add mocked attribute **//
But this one is calling the constructor of the attribute where I set up the external source. When I put this constructor in a try/catch and silently disposing the exception, I have an error on IsAuthorizedAttribute.Object object can't be found.
What are other options to not fire the attribute?
Constructors should not access externals; otherwise it will be difficult to bypass for testing, as you know.
A simple way is to make static bool field to bypass. This does not look so good but maybe enough.
public class IsAuthorizedAttribute : CodeAccessSecurityAttribute
{
// set true in the test initialization
private static bool s_byPass;
public IsAuthorizedAttribute(SecurityAction action) : base(action)
{
if (!s_byPass)
{
// setup
}
}
private static bool IsAuthorised(string entityObject, string field, char expected, ServicesConfiguration servicesConfiguration)
{
if (s_byPass) { return true; }
//check external stuff
}
}
Another better approach is to extract the external dependency to another class so that you can mock it. Mocking external dependencies is a typical pattern of a unit test.
public class IsAuthorizedAttribute : CodeAccessSecurityAttribute
{
// set mock here in the test initialization.
// I assume external accessor can be a static field.
private static ExternalAccessor m_accessor = new ExternalAccessor();
private static bool IsAuthorised(string entityObject, string field, char expected, ServicesConfiguration servicesConfiguration)
{
return m_accessor.Check();
}
}
public class ExternalAccessor
{
private bool m_initialized;
private void Setup()
{
// setup
m_initialized = true;
}
public virtual bool Check()
{
// You can call setup anytime but the constructor.
if (!m_initialized) { Setup(); }
// check external stuff
}
}

How can I improve and/or modularize my handling of event based tasks?

So I have a server and I'm making calls to it through a wrapped up WebSocket (WebSocket4Net) and one of the requirements of the library I'm building is the ability to await on the return of the request. So I have a class MessageEventHandler that contains events that are triggered by the class MessageHandler as messages come in.
MessageEventHandler ex.
public class MessageEventHandler : IMessageEventHandler
{
public delegate void NodeNameReceived(string name);
public event Interfaces.NodeNameReceived OnNodeNameReceived;
public void NodeNameReceive(string name)
{
if (this.OnNodeNameReceived != null)
{
this.OnNodeNameReceived(name);
}
}
}
MessageHandler ex.
public class MessageHandler : IMessageHandler
{
private IMessageEventHandler eventHandler;
public MessageHandler(IMessageEventHandler eventHandler)
{
this.eventHandler = eventHandler;
}
public void ProcessDataCollectorMessage(string message)
{
var serviceMessage = JsonConvert.DeserializeObject<ServiceMessage>(message);
switch (message.MessageType)
{
case MessageType.GetNodeName:
{
var nodeName = serviceMessage.Data as string;
if (nodeName != null)
{
this.eventHandler.NodeNameReceive(nodeName);
}
break;
}
default:
{
throw new NotImplementedException();
}
}
}
Now building upon those classes I have the class containing my asynchronous function that handles the call to get the node name.
public class ClientServiceInterface : IClientServiceInterface
{
public delegate void RequestReady(ServiceMessage serviceMessage);
public event Interfaces.RequestReady OnRequestReady;
public int ResponseTimeout { get; private set; }
private IMessageEventHandler messageEventHandler;
public ClientServiceInterface(IMessageEventHandler messageEventHandler, int responseTimeout = 5000)
{
this.messageEventHandler = messageEventHandler;
this.ResponseTimeout = responseTimeout;
}
public Task<string> GetNodeNameAsync()
{
var taskCompletionSource = new TaskCompletionSource<string>();
var setHandler = default(NodeNameReceived);
setHandler = name =>
{
taskCompletionSource.SetResult(name);
this.messageEventHandler.OnNodeNameReceived -= setHandler;
};
this.messageEventHandler.OnNodeNameReceived += setHandler;
var ct = new CancellationTokenSource(this.ResponseTimeout);
var registration = new CancellationTokenRegistration();
registration = ct.Token.Register(
() =>
{
taskCompletionSource.TrySetCanceled();
this.messageEventHandler.OnNodeNameReceived -= setHandler;
registration.Dispose();
},
false);
var serviceMessage = new ServiceMessage() { Type = MessageType.GetNodeName };
this.ReadyMessage(serviceMessage);
return taskCompletionSource.Task;
}
}
As you can see I wouldn't call it pretty and I apologize if anyone threw up a little reading it. But this is my first attempt at wrapping a Task with Asynchronous Event. So with that on the table I could use some help.
Is there a better way to accomplish what I'm trying to achieve here? Remembering that I want a user of the library to either subscribe to the event and listen for all callbacks OR they can simply await the return depending on
their needs.
var nodeName = await GetNodeNameAsync();
Console.WriteLine(nodeName);
or
messageEventHandler.OnNodeNameReceived += (name) => Console.WriteLine(name);
GetNodeNameAsync();
Alternatively if my approach is actually 'good' can anyone provide any advice as to how I can write a helper function to abstract out setting up each function in this way? Any help would be greatly appreciated.
So I've written a couple classes to solve the problem I was having. The first of which is my CallbackHandle class which contains the task inside the TaskCompletionSource so each time that a request is made in my example a new callback handle is created.
public class CallbackHandle<T>
{
public CallbackHandle(int timeout)
{
this.TaskCompletionSource = new TaskCompletionSource<T>();
var cts = new CancellationTokenSource(timeout);
cts.Token.Register(
() =>
{
if (this.Cancelled != null)
{
this.Cancelled();
}
});
this.CancellationToken = cts;
}
public event Action Cancelled;
public CancellationTokenSource CancellationToken { get; private set; }
public TaskCompletionSource<T> TaskCompletionSource { get; private set; }
}
Then I have a 'handler' that manages the handles and their creation.
public class CallbackHandler<T>
{
private readonly IList<CallbackHandle<T>> callbackHandles;
private readonly object locker = new object();
public CallbackHandler()
{
this.callbackHandles = new List<CallbackHandle<T>>();
}
public CallbackHandle<T> AddCallback(int timeout)
{
var callback = new CallbackHandle<T>(timeout);
callback.Cancelled += () =>
{
this.callbackHandles.Remove(callback);
callback.TaskCompletionSource.TrySetResult("Error");
};
lock (this.locker)
{
this.callbackHandles.Add(callback);
}
return callback;
}
public void EventTriggered(T eventArgs)
{
lock (this.locker)
{
if (this.callbackHandles.Count > 0)
{
CallbackHandle<T> callback =
this.callbackHandles.First();
if (callback != null)
{
this.callbackHandles.Remove(callback);
callback.TaskCompletionSource.SetResult(eventArgs);
}
}
}
}
}
This is a simplified version of my actual implementation but it should get someone started if they need something similar. So to use this on my ClientServiceInterface class in my example I would start by creating a class level handler and using it like this:
public class ClientServiceInterface : IClientServiceInterface
{
private readonly CallbackHandler<string> getNodeNameHandler;
public ClientServiceInterface(IMessageEventHandler messageEventHandler, int responseTimeout = 5000)
{
this.messageEventHandler = messageEventHandler;
this.ResponseTimeout = responseTimeout;
this.getNodeNameHandler = new
CallbackHandler<string>();
this.messageEventHandler.OnNodeNameReceived += args => this.getNodeNameHandler.EventTriggered(args);
}
public Task<string> GetNodeNameAsync()
{
CallbackHandle<string> callbackHandle = this.getNodeNameHandler.AddCallback(this.ResponseTimeout);
var serviceMessage = new ServiceMessage
{
Type = MessageType.GetNodeName.ToString()
};
this.ReadyMessage(serviceMessage);
return callbackHandle.TaskCompletionSource.Task;
}
// Rest of class declaration removed for brevity
}
Which is much better looking than what I had before (at least in my opinion) and it's easy to extend.
For starters follow a thread-safe pattern:
public void NodeNameReceive(string name)
{
var evt = this.OnNodeNameReceived;
if (evt != null)
{
evt (name);
}
}
If you do not take a reference to the event object it can be set to null between the time you check null and call the method.

How to set foreach item expectation in rhino mocks 3.6

I want to be able to do something like this:
IProcessDetails detailprocessor = MockRepository.GenerateMock();
detailprocessor.Expect(p => p.Process(null))
.Repeat.ForEachItemIn(details);
Here is the code I want to test
public class Detail
{
}
public class Master
{
public Master(IEnumerable<Detail> details)
{
Details = details;
}
public IEnumerable<Detail> Details { get; private set; }
}
public interface IProcessDetails
{
void Process(Detail detail);
}
public interface IProcessMasters
{
void Process(Master master);
}
public class MasterProcessor :
IProcessMasters
{
public MasterProcessor(IProcessDetails detailProcessor)
{
DetailProcessor = detailProcessor;
}
IProcessDetails DetailProcessor;
#region IProcessMasters Members
public void Process(Master master)
{
foreach (var detail in master.Details)
{
DetailProcessor.Process(detail);
}
}
#endregion
}
I want to verify that each detail is processed but only once and I'm not sure how to do it
Here is my current test:
public void GivenAMasterWithDetailsWhenProcessedThenAllDetailsAreProcessed()
{
IEnumerable<Detail> details = new List<Detail>()
{
new Detail(),
new Detail()
};
Master master = new Master(
details);
IProcessDetails detailprocessor = MockRepository.GenerateMock<IProcessDetails>();
//what I want or something similar
//detailprocessor.Expect(p => p.Process(null))
// .Repeat.ForEachItemIn(details);
//what it appears I can do
detailprocessor.Expect(p => p.Process(null))
.Constraints(List.OneOf(details))
.Repeat.Times(details.Count());
IProcessMasters masterprocessor = new MasterProcessor(
detailprocessor);
masterprocessor.Process(master);
detailprocessor.VerifyAllExpectations();
}
SOLVED -- I figured it out:
What I needed was a better understanding of rhino mocks.
Using a strict mock and a simple foreach loop solved the problem
here is my test now:
public void GivenAMasterWithDetailsWhenProcessedThenAllDetailsAreProcessed()
{
IEnumerable<Detail> details = new List<Detail>()
{
new Detail(),
new Detail()
};
Master master = new Master(
details);
IProcessDetails detailprocessor = MockRepository.GenerateStrictMock<IProcessDetails>();
foreach (var detail in details)
{
detailprocessor.Expect(p => p.Process(detail))
.Repeat.Once();
}
IProcessMasters masterprocessor = new MasterProcessor(
detailprocessor);
masterprocessor.Process(master);
detailprocessor.VerifyAllExpectations();
}

Categories

Resources