Rhino Mocks and PRISM EventAggregator - wpf

I need to do something that seems quite simple, but I cant seem to achieve it.
I need to be able to write a unit test that calls the action and filter delegates of any subscription to an eventaggregator event.
For example, in my class that needs to be tested I have the following code:
this.eventAggregator.GetEvent<RiskDataViewsJournalChangedEvent>().Subscribe(
this.OnViewRequestPublished, ThreadOption.UIThread, false, this.EventFilter);
and I want my test to call the this.OnViewRequestPublished method and this.EventFilter method.
Ive tried using an instance of the EventAggregator class in my test but the events never get fired without a dispatcher present, which is not helpfull in a unit test.
Therefore I want to use Rhino Mocks, but I cant get my head around how to achieve what I need.
Thanks
Dean

Ive solved it myself - here is my code (ignore the EventToken references, this is something ive created myself for event filtering).
[Test]
public void RiskDataGridViewModelEventSubscriptionTests()
{
// event token
var tok = new EventToken();
// mock event aggregator
var agg = MockRepository.GenerateStub<IEventAggregator>();
// my target class subscribes to 3 events in its constructor
var evt1 = MockRepository.GenerateStub<RiskDataViewsJournalChangedEvent>();
var evt2 = MockRepository.GenerateStub<RiskDataViewsJournalChangingEvent>();
var evt3 = MockRepository.GenerateStub<RiskDataViewRequestPublishEvent>();
// ensure mocked event classes are returned
agg.Stub(x => x.GetEvent<RiskDataViewsJournalChangedEvent>()).Return(evt1);
agg.Stub(x => x.GetEvent<RiskDataViewsJournalChangingEvent>()).Return(evt2);
agg.Stub(x => x.GetEvent<RiskDataViewRequestPublishEvent>()).Return(evt3);
// instantiate target class - in my class the events get subscribed to in the constructor
new RiskDataGridViewModel(agg, tok, null);
// get the parameters passed to the subscribe method
var args1 = evt1.GetArgumentsForCallsMadeOn(s => s.Subscribe(null));
var args2 = evt2.GetArgumentsForCallsMadeOn(s => s.Subscribe(null));
var args3 = evt3.GetArgumentsForCallsMadeOn(s => s.Subscribe(null));
// invoke filters
((Predicate<EventParameter<IRiskDataViewResultItem>>)args1[0][3]).Invoke(new EventParameter<IRiskDataViewResultItem>(null, tok));
((Predicate<EventParameter>)args2[0][3]).Invoke(new EventParameter(null, tok));
((Predicate<EventParameter<ViewRequest>>)args3[0][3]).Invoke(new EventParameter<ViewRequest>(null, tok));
// invoke methods
((Action<EventParameter<IRiskDataViewResultItem>>)args1[0][0]).Invoke(new EventParameter<IRiskDataViewResultItem>(null, tok));
((Action<EventParameter>)args2[0][0]).Invoke(new EventParameter(null, tok));
((Action<EventParameter<ViewRequest>>)args3[0][0]).Invoke(new EventParameter<ViewRequest>(null, tok));
}

/// <remarks>Event.Publish does not work in unit tests when subscribed with ThreadOption.UIThread </remarks>
public void FireGlobalEvent<TEvent, TEventArgs>(TEventArgs args)
where TEvent : CompositePresentationEvent<TEventArgs>, new ()
{
var globalEvent = GetInstance<IEventAggregator>().GetEvent<TEvent>();
var subscriptions = (IEnumerable)globalEvent.GetType().GetProperty("Subscriptions", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(globalEvent);
foreach (DispatcherEventSubscription<TEventArgs> subscription in subscriptions.Cast<object>().ToArray())
{
subscription.Action.Invoke(args);
}
}

Related

How to make the autocomplete handler work in Discord.Net?

I'm sorry if it sound a bit newbie, but i'm stuck trying to get working the AutocompleteHandler.
Following the documentation, i tried to create a very basic autocomplete system :
public class ExampleAutocompleteHandler : AutocompleteHandler
{
public override async Task<AutocompletionResult> GenerateSuggestionsAsync(IInteractionContext context, IAutocompleteInteraction autocompleteInteraction, IParameterInfo parameter, IServiceProvider services)
{
// Create a collection with suggestions for autocomplete
IEnumerable<AutocompleteResult> results = new[]
{
new AutocompleteResult("Name1", "value111"),
new AutocompleteResult("Name2", "value2")
};
// max - 25 suggestions at a time (API limit)
return AutocompletionResult.FromSuccess(results.Take(25));
}
}
In the module :
public class ItemModule : InteractionModuleBase
{
// you need to add `Autocomplete` attribute before parameter to add autocompletion to it
[SlashCommand("command_name", "command_description")]
public async Task ExampleCommand([Summary("parameter_name"), Autocomplete(typeof(ExampleAutocompleteHandler))] string parameterWithAutocompletion)
=> await RespondAsync($"Your choice: {parameterWithAutocompletion}");
}
I tried to register the handler as a Singleton, but nothing worked. It doesnt display the list when i type the commands.
I appreciate some help. Thanks in advance!
Indeed the documentation is currently missing this.
Just like Slash Commands, you need to handle the Autocompletion event and forward it to your interaction service.
On your discord client, handle the event
_client.AutocompleteExecuted += async (SocketAutocompleteInteraction arg) => {
var context = new Discord.Interactions.InteractionContext(_client, arg, arg.Channel);
await _interactionService.ExecuteCommandAsync(context,services: _serviceProvider);
};

RxJS Observables, when to unsubscibe?

I have a couple of questions about Angular. I recently started to experiment with Angular and really cant get a grip of when I should unsubscribe, there is obviously recommended to use the AsyncPipe but in some cases there is no way to use it.
If I subscribe to a HTTP request in a service, does the Observable unsubscribe itself or is it persistent through out the application lifetime?
When I subscribe (without the AsyncPipe) to a HTTP request in a component I could manually unsubscribe in the ngOnDestroy lifecycle hook which is fine but in my case I have a submit method to create account
account.component.html
<account-item>
*ngFor="let account of collection$"
[data]="account"
</account-item>
account.component.ts
public collection$: Observable<Account[]>;
private subscription: Subscription;
constructor(
private service: AccountService
) {}
ngOnInit() {
this.collection$ = this.service.getAll()
}
createAccount(obj) {
this.subscription = this.service.create(obj)
.subscribe(
success => this.collection$ = this.service.getAll(),
error => Observable.throw(error)
);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
From what I know the subscription is now persistent until my AccountComponent is destroyed, but is there a way to use AsyncPipe here instead or is it better for me to subscribe in the service itself?
I've read something about finite and infinite Observables but haven't really understand when a Observable is finite or not.
Another problem I'm facing is that in success => this.collection$ = this.service.getAll() my UI doesn't update with the new account list when I use ChangeDetectionStrategy.OnPush but works just fine with ChangeDetectionStrategy.Default
This is the service method that fetches the account data
getAll() {
return this.http.get(ENDPOINT_ACCOUNT)
.map((response: Response) => response.json().data)
}
What you need to think when dealing with Observables and functionnal programming more globaly is that you don't describe how things are done but describe what things are.
In your example, you collection is the combination of on the one hand the initial fetch from the service and on the other hand, all updates that may occur, so if you want to avoid subscribing in the component, you can do such a thing:
class Foo {
public collection$: Observable < Account[] > ;
private createAccount$ = new Subject<Account>();
constructor(
private service: AccountService
) {}
ngOnInit() {
let initialAccounts = this.service.getAll().share();
let accountUpdate = initialAccounts.switchMap(()=>this.createAccount$.switchMap(account=>{
return this.service.create(account).switchMap(()=>this.service.getAll())
}))
this.collection$ = Observable.merge(initialAccounts,accountUpdate);
}
createAccount(obj:Account) {
this.createAccount$.next(obj);
}
}
We are using here the merge operator to take the data from either initialAccounts or createAccount$. It is always a good thing to combine your observables together and subscribe once, because this way you don't have to imperatively manage your subscription.
The fact is most of the time, you don't need to subscribe() at all.

Async-await seems to use the UI thread

In a view-model I use a factory:
private async Task<BaseData> InitializeAsync()
{
await InstancesAsync();
await ProjectsAsync();
await AdminAsync();
return this;
}
public static async Task<BaseData> CreateAsync()
{
var ret = new BaseData();
return await ret.InitializeAsync();
}
The awaited methods are rather staightforward, with e.g.
var instances = await TaskEx.Run(new Func<List<string>>(() => Agent.GetInstances()));
In the wpf view I want to set the DataContext in the constructor:
Loaded += delegate
{
Dispatcher.Invoke(new Action(async () => { DataContext = await BasisGegevens.CreateAsync(); }));
};
Although it works, I feel rather uncomfortable because the UI thread is used everywhere, also after the callbacks when the awaits complete. What am I missing?
Also I don't understand how to use the factory pattern for the DataContext because without the Invoke above I get the error that a different thread owns the object.
EDIT: using the ideas of Mr. Cleary I get:
Loaded += async (object sender, RoutedEventArgs e) =>
{ DataContext = await BaseData.CreateAsync(); };
public static Task<BaseData> CreateAsync()
{
var ret = new BaseData();
return ret.InitializeAsync();
}
private async Task<BaseData> InitializeAsync()
{
// UI thread here
await InstancesAsync().ConfigureAwait(false);
// thread 'a' here
await ProjectsAsync().ConfigureAwait(false);
// thread 'a' sometimes 'b' here
await AdminAsync().ConfigureAwait(false);
// thread 'a' or 'b' here
return this;
}
This works fine, except I cannot understand how ConfigureAwait(false) works.
Inside the method InstancesAsync() I have the awaited task:
var instances = await TaskEx.Run(new Func<List<string>>(() => Agent.GetInstances()));
After awaiting the repsonse, I return in the UI thread - I never expected that to happen!
Note that ProjectsAsync() and AdminAsync() behave the same, although they start on a worker (or background) thread!
I thougth that ConfigureAwait(true) has the effect of returning in the calling thread (in my case UI thread). I tested that and it is so.
Why do I see this with ConfigureAwait(false) too: because of a nested await, see comments.
I find it most useful to treat the ViewModel as having UI thread affinity. Think of it as the logical UI, even if it's not the actual UI. So all property and observable collection updates on ViewModel classes should be done on the UI thread.
In your async methods, if you don't need to return to the UI thread, then you can use ConfigureAwait(false) to avoid resuming on the UI thread. For example, if your various initialization methods are independent, you could do something like this:
private async Task<BaseData> InitializeAsync()
{
// Start all methods on the UI thread.
var instancesTask = InstancesAsync();
var projectsTask = ProjectsAsync();
var adminTask = AdminAsync();
// Await for them all to complete, and resume this method on a background thread.
await Task.WhenAll(instancesTask, projectsTask, adminTask).ConfigureAwait(false);
return this;
}
Also, any time you have return await, take another look to see if you can just avoid async/await entirely:
public static Task<BaseData> CreateAsync()
{
var ret = new BaseData();
return ret.InitializeAsync();
}
Finally, you should strongly avoid Dispatcher. Your Loaded event could be simplified to this:
Loaded += async ()
{
DataContext = await BasisGegevens.CreateAsync();
};

Silverlight async unit testing

I'm having a weird issue with Silverlight Unit Test Framework. The very first method executed fails, every time. I have a second test with the exact same code, and it passes. The strange thing about the first time it's called is that it actually waits for the timeout and then executes the repository call (underneath it's an HTTP PUT if you care). Here's the code - the first one fails every time, second one passes every time:
[TestMethod]
public void AuthShouldSucceed()
{
var autoResetEvent = new AutoResetEvent(false);
_authRepository.Authenticate(_username, _password, response =>
{
Assert.IsTrue(response);
autoResetEvent.Set();
});
if (!autoResetEvent.WaitOne(Constants.Timeout))
{
Assert.Fail("Test timed out.");
}
}
[TestMethod]
public void AuthShouldSucceed2()
{
var autoResetEvent = new AutoResetEvent(false);
_authRepository.Authenticate(_username, _password, response =>
{
Assert.IsTrue(response);
autoResetEvent.Set();
});
if (!autoResetEvent.WaitOne(Constants.Timeout))
{
Assert.Fail("Test timed out.");
}
}
Edit:
My final solution is a modification of Vladmir's solution:
[TestMethod]
[Asynchronous]
public void AuthShouldSucceed()
{
var complete = false;
var result = false;
_authRepository.Authenticate(_username, _password, response =>
{
complete = true;
result = response;
});
EnqueueConditional(() => complete);
EnqueueCallback(() => Assert.IsTrue(result));
EnqueueTestComplete();
}
If you're using Silverlight Unit Tests Framework try to rewrite your test next way:
[TestMethod]
[Asynchronous]
public void AuthShouldSucceed()
{
var done = false;
var authResult = false;
_authRepository.Authenticate(_username, _password, response =>
{
var done = true;
authResult = response;
});
EnqueueConditional(() => done);
EnqueueCallback(() => Assert.IsTrue(authResult));
EnqueueTestComplete();
}
Your test class should be derived from SilverlightTest class:
[TestClass]
public class MyTests: SilverlightTest
Summarizing all written you should be aware of couple important points in writing Unit Tests for Silverlight.
[Asynchronous]
Attribute of test method shows that you are testing part which includes async operations and want to syncronize them for testing purposes. Asynchrony itself in Silverlight Unit Tests Framework is a bit distorted. All "Enqueue" directives are like "postpone this until something happens" which is not a sense which the traditional programming asynchrony meaning has.
EnqueueConditional
Otherwords means wait until condition will be met. And this is one of the most important parts of async tests. After condition becomes legal all EnqueueCallbacks until the next EnqueueConditional will be executed.
Important note:
EnqueueConditional repeatedly calls the predicate passed to it on a
timer/background thread, checking each time to see if it returns true.
That's why you should avoid using heavy, complex logic within conditions.
EnqueueCallback
Defers code execution until EnqueueConditional that precedes it will met condition. It enqueues an Actoin or an array of Actions.
EnqueueDelay
Enqueues minimum number of milliseconds/time delay before continuing.
EnqueueTestComplete
Enqueues an action to call TestComplete which signals that a test is complete when using Async testing.
Important Note(from documentation):
If you use your own methods for completing, such as an HtmlTimer or
other threading method, it is possible that this call will occur
AFTER the test has timed out when using Timeouts. As such, be very careful as you could complete the call to the next test.
You can use the Asynchronous keyword in the TestMethod
[TestMethod]
[Asynchronous]
[Description("This test checks when NULL is passed")]
public void Testing()
{
bool done = false;
EnvViewModel test = new EnvViewModel ();
.
.
test.AsyncCallBackCompleted += (() => done = true);
EnqueueCallback(() => test.DataCommand.Execute(null));
EnqueueConditional(() => done);
EnqueueCallback(() => Assert.IsTrue(test.AADTDecisionModelList.Count == 0,
"The result does not have any data."));
EnqueueTestComplete();
}
And make a delegate in the ViewModel to be used to every method that is being tested...
public delegate void AsynCallComplete();
public event AsynCallComplete AsyncCallBackCompleted;
public void InformCallbackCompleted()
{
if (AsyncCallBackCompleted != null)
{
AsyncCallBackCompleted();
}
}

Unit testing with MVVM Light & DispatcherHelper

I have a SL4 app that utilizes the MVVM Light Toolkit. Within a view model, I call a data service that retrieves data from an OData service. Within the VM, I am using the DispatcherHelper utility class (part of MVVM Light) to update the property on the VM from the data in the callback I pass into the data service. For instance, my view model method looks like this:
public string CurrentUserLogin {
get {
if (string.IsNullOrEmpty(_currentUserLogin))
RetrieveCurrentUserLogin();
return string.IsNullOrEmpty(_currentUserLogin) ? _currentUserLogin : _currentUserLogin.Replace(#"\\", #"\");
}
set {
if (_currentUserLogin != value) {
_currentUserLogin = value;
RaisePropertyChanged(CurrentUserLoginPropertyName);
}
}
}
private void RetrieveCurrentUserLogin() {
DataService.GetCurrentUserLogin(result => {
DispatcherHelper.UIDispatcher.BeginInvoke(() => {
CurrentUserLogin = result;
});
});
}
And here's what my data service looks like:
public void GetCurrentUserLogin(Action<string> callback) {
// create query request
var query = OnDemandContext.CreateQuery<string>("GetCurrentUserLogin");
var request = (HttpWebRequest)WebRequest.Create(query.RequestUri);
request.BeginGetResponse(asyncResult => {
var responseStream = request.EndGetResponse(asyncResult).GetResponseStream();
var responseDocument = XDocument.Load(responseStream);
callback(responseDocument.Root.Value);
}, null);
}
Everything works great when I run my SL application. However the problem I have is when I try to write unit tests against it using the SL Unit Testing Framework. I can test my data service without an issue, but it seems the DispatcherHelper is throwing a wrench into all my tests as the DispatcherHelper.UIDispatcher is always null when fired. I'm assuming it has something to do with the initlization (which is in my SL app's Application_Startup()). I tried initializing it in my test app but that isn't helping. I've also tried using DispatcherHelper.CheckBeginInvokeOnUI() but that has no effect on the issue.
Ideas?
AC,
I just created a simple SL UT project and I did this in the App.XAML.CS
private void Application_Startup(object sender, StartupEventArgs e)
{
RootVisual = UnitTestSystem.CreateTestPage();
DispatcherHelper.Initialize();
}
Then I set this as the test (in the tests.cs):
[TestMethod]
public void TestMethod1()
{
Assert.IsNotNull(DispatcherHelper.UIDispatcher, "UI Dispatcher should not be null");
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
// Do nothing
var x = 1;
});
}
That worked for me. I even set a break point on the "var x = 1;" and it hit the breakpoint. Does this solve your problem? (if so please mark it as the answer).

Resources