Make unit test pass only when callback is called - c#

I'm trying to unit test a bit of code to make sure that a callback is invoked, but it seems that even without an "Assert"-call in the method it will pass. Consider the code example below:
public void Show_ShowSomeAdFormat_CallbackShouldBeInvoked()
{
AdManager adManager = new AdManager();
adManager.Show<VideoTestAdFormat>((response) =>
{
//Assert.Pass(); <--- With or without this, the test will pass.
//I need it to only pass if it reaches this. How is it done?
});
}
If you read the comments I think you will understand what I am after.
Thank you!

Use a captured bool.
public void Show_ShowSomeAdFormat_CallbackShouldBeInvoked()
{
AdManager adManager = new AdManager();
bool callbackInvoked = false;
adManager.Show<VideoTestAdFormat>((response) => callbackInvoked = true);
// If the callback is invoked asynchronously,
// you'll need a way to wait here for Show to complete.
Assert.IsTrue(callbackInvoked);
}
EDIT:
If you're using .NET 4, you might have Show return a Task that completes when Show is done doing work. In earlier .NET versions, you can return a ManualResetEvent. "Return" can be a return value, or an overload of Show with an out parameter.

Related

Delay Returns with NSubstitute

I have an interface IDiscosClient, for testing/demo purposes while I'm developing the app, I want a mock to return a new model when the .GetSingle<T>() method is called with a random delay of between 1 and 5 seconds. This is mostly so I can see that all of my various loading spinner components and whatnot work.
So, I thought I'd be able to do something like this:
Fixture fixture = new();
fixture.Customize(new DiscosModelFixtureCustomizationNoLinks());
builder.Services.AddTransient(_ =>
{
IDiscosClient client = Substitute.For<IDiscosClient>();
DiscosObject obj = fixture.Create<DiscosObject>();
client.GetSingle<DiscosObject>(Arg.Any<string>()).Returns(Task.Delay(Random.Shared.Next(1000,5000)).ContinueWith(_ => obj));
return client;
});
However, while there seems to be a delay when I first call the method, once this has resolved, it just seems to return the completed task with the same model in it every time I call it for that IDiscosClient instance.
Is there a simple enough way to accomplish this?
So the issue is that the code above only creates a fresh Task the first time and then returns the same one (which has already completed) each subsequent time.
To fix this, we can either change the code above to:
client.GetSingle<DiscosObject>(Arg.Any<string>()).Returns(_ => Task.Delay(Random.Shared.Next(1000,5000)).ContinueWith(_ => obj));
Or, for legibilities sake, we can extract it into a method and make the whole code block:
builder.Services.AddTransient(_ =>
{
IDiscosClient client = Substitute.For<IDiscosClient>();
client.GetSingle<DiscosObject>(Arg.Any<string>()).Returns(GetDiscosObject);
return client;
});
async Task<DiscosObject> GetDiscosObject(CallInfo _)
{
await Task.Delay(Random.Shared.Next(1000, 5000));
return fixture.Create<DiscosObject>();
}

Alternative in a situation of recurring Task demand

I have observer module which takes care of subscriptions of some reactive stream I have created from Kafka. Sadly I need to Poll in order to receive messages from kafka, so I need to dedicate one background thread for that. My first solution was this one:
public void Poll()
{
if (Interlocked.Exchange(ref _state, POLLING) == NOTPOLLING)
{
Task.Run(() =>
{
while (CurrentSubscriptions.Count != 0)
{
_consumer.Poll(TimeSpan.FromSeconds(1));
}
_state = NOTPOLLING;
});
}
}
Now my reviewer suggested that I should Task because it have statuses and can be checked if they are running or not. This led to this code:
public void Poll()
{
// checks for statuses: WaitingForActivation, WaitingToRun, Running
if (_runningStatuses.Contains(_pollingTask.Status)) return;
_pollingTask.Start(); // this obviously throws exception once Task already completes and then I want to start it again
}
Task remained pretty much the same but check changed, now since my logic is that I want to start polling when I have subscriptions and stop when I don't I need to sort of re-use the Task, but since I can't I am wondering do I need to go back to my first implementation or is there any other neat way of doing this that right now I am missing?
I am wondering do I need to go back to my first implementation or is there any other neat way of doing this that right now I am missing?
Your first implementation looks fine. You might use a ManualResetEventSlim instead of enum and Interlocked.Exchange, but that's essentially the same... as long as you have just two states.
I think I made a compromise and removed Interlocked API for MethodImpl(MethodImpl.Options.Synchronized) it lets me have simple method body without possibly confusing Interlocked API code for eventual newcomer/inexperienced guy.
[MethodImpl(MethodImplOptions.Synchronized)]
public void Poll()
{
if (!_polling)
{
_polling = true;
new Task(() =>
{
while (_currentSubscriptions.Count != 0)
{
_consumer.Poll(TimeSpan.FromSeconds(1));
}
_polling = false;
}, TaskCreationOptions.LongRunning).Start();
}
}

How do I exit a While(true) loop with a key?

So I have this while loop that looks something like this.
bool isTrue = true;
while(isTrue)
{
//Do work over and over
}
I'm wondering.. Can I add if statement that checks if a key has been pressed during the current loop to break out of it? WITHOUT INTERRUPTING THE LOOP so some sort of async task maybe? Because it's automating the task over and over again. For the sake of the question let's say it's printing out "Hello World!" to the console every third second.
It's not a great idea to use a while loop like this. The suggestion that you're writing something to the console every one third of a second suggests that you're using a Thread.Sleep to wait that time out.
It's far better to use a timer or some other library that let's you do this kind of thing. I'd suggest Microsoft's Reactive Framework. Then you can do this:
IDisposable subscription =
Observable
.Interval(TimeSpan.FromSeconds(1.0 / 3.0))
.TakeWhile(_ => !Console.KeyAvailable)
.Subscribe(_ =>
{
Console.WriteLine("Hello World!");
});
Job done. If you want to stop the subscription early just call subscription.Dispose().
NuGet "System.Reactive" to get the bits you need.
Couldn't you simply just have it as a condition in your while loop. While (true and condition) {} or you can just do an if statement in the body with a break.
You'd probably want to set up a class so that you can have a shared property available in different methods.
public class MyLooper
{
private bool KeepLooping { get; set; } = true;
public void OnKeyPressed()
{
KeepLooping = false;
}
public void StartLoop()
{
Task.Factory.StartNew(() => {
while (KeepLooping)
{
Debug.WriteLine("Hello World");
}
});
}
}
You haven't indicated where you'd be running this e.g. server, desktop app, console etc but you may encounter and need to tackle thread safety issues.

wpf- How to wait for result from Dispatcher.invokeAsync before control is passed to next statement

I have a Lazy method in which I have called Dispacter
private Lazy<ObservableCollection<WidgetTasksSummary>> LazyTasksSummaryHelper()
{
return new Lazy<ObservableCollection<WidgetTasksSummary>>(() =>
{
Dispatcher.InvokeAsync(() => GetActivityItemsCount());
return new ObservableCollection<WidgetTasksSummary>(GetMyTasksSummary());
});
}
private ActivitySearchResultList ActivityItemsCount{get;set;}
private ObservableCollection<WidgetTasksSummary> GetMyTasksSummary()
{
//Do Something
}
private async Task GetActivityItemsCount()
{
try
{
ActivityItemsCount = await Ioc.Resolve<IServiceCall>().InvokeAsync<IActivityManager, ActivitySearchResultList>(this.MakeWeakFunc<IActivityManager, ActivitySearchResultList>((service) =>
service.GetActivityCounts(activityFilter)));
}
}
I am using ActivityItemsCount Property to assign result of api call.
After LazyTasksHelper is called, the control is sent to GetMyTasksSummary() after dispacter.
I want to wait for the dispacter to make api call and store result in property. And then continue execution.
I am using the property in getTasksSummary. So, I need it to be filled first in action called in dispacter then enter into method GetMyTasksSummary()
You need to change to AsyncLazy and await that call.
I found a solution. I moved all the api calls and other stuffs into another ViewModel and used Lazy<ViewModel>. In this way, viewmodel takes care of calling api and setting up necessary property.

Await stops execution of thread and never continues

I have the following:
public async Task<bool> SearchForUpdatesAsync()
{
return await TaskEx.Run(() =>
{
if (!ConnectionChecker.IsConnectionAvailable())
return false;
// Check for SSL and ignore it
ServicePointManager.ServerCertificateValidationCallback += delegate { return (true); };
var configurations = UpdateConfiguration.Download(UpdateConfigurationFileUri, Proxy);
var result = new UpdateResult(configurations, CurrentVersion,
IncludeAlpha, IncludeBeta);
if (!result.UpdatesFound)
return false;
_updateConfigurations = result.NewestConfigurations;
double updatePackageSize = 0;
foreach (var updateConfiguration in _updateConfigurations)
{
var newPackageSize = GetUpdatePackageSize(updateConfiguration.UpdatePackageUri);
if (newPackageSize == null)
throw new SizeCalculationException(_lp.PackageSizeCalculationExceptionText);
updatePackageSize += newPackageSize.Value;
_packageOperations.Add(new UpdateVersion(updateConfiguration.LiteralVersion),
updateConfiguration.Operations);
}
TotalSize = updatePackageSize;
return true;
});
}
As you can see I'm using Microsoft.Bcl.
Now in my other class I wrote this code in a normal void:
TaskEx.Run(async delegate
{
// ...
_updateAvailable = await _updateManager.SearchForUpdatesAsync();
MessageBox.Show("Test");
});
The problem I have is that it executes _updateAvailable = await _updateManager.SearchForUpdatesAsync(); and then it doesn't continue the thread, it just stops as if there is nothing after that call. Visual Studio also tells me this after a while: Thread ... exited with code 259, so something seems to be still alive.
I debugged through it to search for any exceptions that could maybe be swallowed, but nothing, everything works fine and it executes the return-statement.
And that is what I don't understand, I never see the MessageBox and/or no code beyond this line's being executed.
After I talked to some friends, they confirmed that this shouldn't be. Did I make a horrible mistake when implementing async-await?
Thanks in advance, that's actually all I can say about that, I got no more information, I appreciate any tips and help as far as it's possible.
The main issue that you're having is that you're unnecessarily wrapping your method in TaskEx.Run() and you are probably experiencing deadlock somewhere.
Your method signature is currently:
public async Task<bool> SearchForUpdatesAsync()
This means the following:
async --> Doesn't actually do anything, but provides a "heads up" that await might be called within this method and that this method can be used as a runnable Task.
Task --> This method returns a runnable task that can be run asynchronously on the threadpool
<bool> --> This method actually returns bool when awaited.
The await TaskEx.Run() is unnecessarily since this says run this method and then don't return until after a value is available. This is most likely causing a synchronization problem. Removing this construct will make your method work properly, however you'll notice that now you have no reason to even include the async operator or the Task<T> portion since the method is actually synchronous anyway. Usually you're only going to use async identifier on the method signature if you have methods that you are going to call await on them.
Instead you have two options.
Whenever you want to call SearchForUpdates() you can wrap this in a Task<bool>.Run() to run it asynchronously (or the Bcl equivalent)
Since you are using WinForms you might be better off using a BackgroundWorker and just calling this method within it.
Regarding using the async-await pattern I think that this is a great article to use to make sure you're following best practices: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
The best practice is to have async all the way through your layers, and then call await or less desirably .Wait() / .Result at the final use site.
Also, try to keep your UI calls separate from the backend work, since you can run into synchronicity/thread-context issue.
public class WinFormsCode
{
private async Task WinsFormCodeBehindMethodAsync()
{
var updatesAvailable = await _updateManager.SearchForUpdatesAsync();
MessageBox.Show("Updates Available: " + updatesAvailable.ToString());
}
private void WinsFormCodeBehindMethodSync()
{
var updatesAvailable = _updateManager.SearchForUpdatesAsync().Result;
MessageBox.Show("Updates Available: " + updatesAvailable.ToString());
}
}
public class UpdateManager
{
public async Task<bool> SearchForUpdatesAsync()
{
return true;
}
}

Categories