xUnit not always catching async exception - c#

I created a xUnit test that calls a method twice to ensure the 2nd call throws an exception saying the class instance is busy. Pretty straightforward.
[Fact]
public async Task RunTwice() {
var P = Create();
Task T1 = Task.Run(() => P.Run("test", null));
Task T2 = Task.Run(() => P.Run("test", null));
await Assert.ThrowsAsync<InvalidOperationException>(() => Task.WhenAll(new Task[] { T1, T2 }));
}
Problem is, this test will randomly either succeed or fail!
The Run method starts like this
public IProcess WorkProcess;
private readonly object lockToken = new object();
public virtual CompletionStatus Run(string fileName, string arguments) {
IProcess P;
lock (lockToken) {
if (WorkProcess != null)
throw new InvalidOperationException();
P = factory.Create();
WorkProcess = P;
}
...
Is this a buy in xUnit or am I doing something wrong? I know async support was added in xUnit 1.9. I'm using v2.4.1.
When I look at debug information, when the test fails, the exception is still thrown.

Looks like you're doing something wrong. Task.Run just schedules the work to the thread pool. Perhaps they'll try to call Run at the same time, perhaps one will happen after the other. There's a race condition inherent in what you're trying to do.
To solve this, you'll need some way for the unit test to control the code within Run and "pause" it.

Related

"Thread was being aborted." in test that tests code that fires a delegate on a background thread

I have some old code that I'm trying to writes tests for. the code parses a log file (on a background thread) and when finished fires off a passed in delegate.
i.e.
public delegate void finread(LogData l, LRParseState l, string e="");
void Thread_ParseLog(object info) {
var info = ti as ThreadInfo;
// some time later
info.fin(log, state, error);
}
public static void ParseErrorLog(string log, finread fin){
var pts = new ParameterizedThreadStart(Thread_ParseLog);
new Thread(pts).Start(new ThreadInfo(log, fin));
}
The code is production code and every thing works ok and has done for a long time, but when I try and test it I get the "Thread was being aborted." exception raised in the Thread_ParseLog method.
The test looks like this:
void llt(string name, Action<LogData, LRParseState> test) {
finread d = (LogData l, LRParseState s, string e) => {
test(l, s);
};
LogReader.ParseErrorLog(name, d);
}
[TestMethod]
public void Create_LogReader_Big_Log() {
llt(ERROR_LOG, (log, state) => {
Assert.IsTrue(log != null); // never get here!
});
}
The test data is largeish, about 55mb, which takes about 500ms to process normally.
I'm also getting errors in the output window:
Exception thrown: 'System.Threading.ThreadAbortException' in
mscorlib.dll System.AppDomainUnloadedException: Attempted to access an
unloaded AppDomain. This can happen if the test(s) started a thread
but did not stop it. Make sure that all the threads started by the
test(s) are stopped before completion.
Which seem to point at some kind of thread sync problems, but there's nothing i can do about the code i'm testing.
It's, obviously, the way I've written my test and I can fix it by changing my test but I'm not sure why its happening.
TIA.
Use synchronization mechanisms like for example a ManualResetEvent to wait for the asynchronous parts of the test to finish before leaving the test method.
[TestMethod]
public void Create_LogReader_Big_Log() {
// Use this event to wait until the asynchronous code has been executed
// before leaving the test method
ManualResetEvent resetEvent = new ManualResetEvent(false);
LogData logDataReceived = null;
llt(ERROR_LOG, (log, state) => {
logDataReceived = log;
// Signal that the test has reached the end
resetEvent.Set();
});
// Wait for the event to be set
resetEvent.WaitOne();
// Additionally wait for a grace period to allow the other thread to fully terminate
Thread.Sleep(500);
// Now perform the asserts on the received data
Assert.IsTrue(logDataReceived != null);
}
Use an async test method and give it a small delay to run.
[TestMethod]
public async Task Create_LogReader_Big_Log()
{
llt(ERROR_LOG, (log, state) => {
Assert.IsTrue(log != null); // never get here!
});
await Task.Delay(3000);
}

Testing task delay

Not sure how to achieve this. I'm trying to unit test a method which waits a few minutes, see here:
internal class JctRestartViaSmsAttemptTestRunner : ITestRunner<JctRestartViaSmsAttempt>
{
private readonly IMayWantMonitoring _queues;
private readonly IAppSettings _appSettings;
public JctRestartViaSmsAttemptTestRunner(IMayWantMonitoring queues, IAppSettings appSettings)
{
_queues = queues;
_appSettings = appSettings;
}
public JctTest Execute(JctRestartViaSmsAttempt jctMessageType)
{
// after five minutes, publish an event to check if the JCT logged in
var jctLoggedInTimeOut = TimeSpan.FromMinutes(double.Parse(_appSettings["JctLogInTimeOut"]));
var message = new JctRestartViaSmsValidate(jctMessageType.Imei);
Task.Delay(jctLoggedInTimeOut)
.ContinueWith(x => _queues.Publish(message));
// reset test values
return new JctTest("6", jctMessageType.Imei, null, null, null);
}
}
This is my test method but I can't manage to mock the task delay stuff.
[Test]
public void TaskTest()
{
// arrange
var imei = _fixture.Create<string>();
_appSettings.Setup(c => c["JctLogInTimeOut"]).Returns("5");
var message = _fixture.Build<JctRestartViaSmsAttempt>()
.With(x => x.Imei, imei)
.Create();
var sut = _fixture.Create<JctRestartViaSmsAttemptTestRunner>();
// act
sut.Execute(message);
// assert
_queues.Verify(x => x.Publish(It.Is<JctRestartViaSmsValidate>(y => y.Imei == imei)));
}
This is the error raised:
Moq.MockException : Expected invocation on the mock at least once,
but was never performed: x =>
x.Publish(It.Is(y => y.Imei == .imei)) No
setups configured. No invocations performed. at
Moq.Mock.ThrowVerifyException(MethodCall expected, IEnumerable1
setups, IEnumerable1 actualCalls, Expression expression, Times times,
Int32 callCount) at Moq.Mock.VerifyCalls(Interceptor
targetInterceptor, MethodCall expected, Expression expression, Times
times) at Moq.Mock.Verify[T](Mock1 mock, Expression1 expression,
Times times, String failMessage) at Moq.Mock1.Verify(Expression1
expression) at
JustEat.PrivateAPN.Worker.UnitTests.TestRunners.JctRestartViaSmsAttemptTestFixture.RunJctTest_WhenRestartAttemptSmsIsSent_ShouldPublishJctRestartValidateMessageWithTheRightImei()
in
I know I need to customize/configure my fixture in order to get into the callback but I'm not sure how to do it, any help would be very appreciated
You shouldn't be firing a Task off without awaiting for it to finish when your test and other things may rely on the result.
So I suggest you change to:
internal class JctRestartViaSmsAttemptTestRunner : ITestRunner<JctRestartViaSmsAttempt>
{
private readonly IMayWantMonitoring _queues;
private readonly IAppSettings _appSettings;
public JctRestartViaSmsAttemptTestRunner(IMayWantMonitoring queues, IAppSettings appSettings)
{
_queues = queues;
_appSettings = appSettings;
}
public async Task<JctTest> ExecuteAsync(JctRestartViaSmsAttempt jctMessageType)
{
// after five minutes, publish an event to check if the JCT logged in
var jctLoggedInTimeOut = TimeSpan.FromMinutes(double.Parse(_appSettings["JctLogInTimeOut"]));
var message = new JctRestartViaSmsValidate(jctMessageType.Imei);
// this will now delay in a non blocking fashion.
await Task.Delay(jctLoggedInTimeOut);
_queues.Publish(message);
// reset test values
return new JctTest("6", jctMessageType.Imei, null, null, null);
}
}
Another reason for awaiting is that you have this _queues if you intend to read from that later on, you can never guarantee the contents because there may still be a thread in the threadpool processing the Task.Delay.
Alternative
If you can't change the signature of the method then you will have to go with the Thread.Sleep() which will block the current thread until it has finished.
Because you did specify Task.Delay I would assume you are using it for the benefits of non blocking.
If you were to use Thread.Sleep() you may want to consider running the JctRestartViaSmsAttemptTestRunner in a Task.Run() so that is will only block the thread it is running on.
internal class JctRestartViaSmsAttemptTestRunner : ITestRunner<JctRestartViaSmsAttempt>
{
private readonly IMayWantMonitoring _queues;
private readonly IAppSettings _appSettings;
public JctRestartViaSmsAttemptTestRunner(IMayWantMonitoring queues, IAppSettings appSettings)
{
_queues = queues;
_appSettings = appSettings;
}
public JctTest Execute(JctRestartViaSmsAttempt jctMessageType)
{
// after five minutes, publish an event to check if the JCT logged in
var jctLoggedInTimeOut = TimeSpan.FromMinutes(double.Parse(_appSettings["JctLogInTimeOut"]));
var message = new JctRestartViaSmsValidate(jctMessageType.Imei);
Thread.Wait(jctLoggedInTimeOut.Milliseconds);
_queues.Publish(message);
// reset test values
return new JctTest("6", jctMessageType.Imei, null, null, null);
}
}
Tests
If you have a method that does return Task<> then you will have to use .Result if you can't have async test method signatures. Which shouldn't be an issue if you run all your tests in serial. If you don't know why .Result and .Wait() are bad read here
So for your async version:
JctTest test = runner.ExecuteAsync().Result;
And your non async version stays the same.
I think the problem is the test is allowed to finish. Your creating a new task after the delay to start but the test function itself completes before this.
I am not overly familiar with the testing framework. But I would expect you want to use .Wait() on the end of the task. This will create a blocking task and your main test thread will be blocked until the delay is finished.
The execute method is not realy testable, because it starts a delayed task, but you are not able to manage that task from outside.
So, you could try to work with Thread.Sleep which is not nice but should work:
[Test]
public void TaskTest()
{
// arrange
var imei = _fixture.Create<string>();
_appSettings.Setup(c => c["JctLogInTimeOut"]).Returns("1");
var message = _fixture.Build<JctRestartViaSmsAttempt>()
.With(x => x.Imei, imei)
.Create();
var sut = _fixture.Create<JctRestartViaSmsAttemptTestRunner>();
// act
sut.Execute(message);
// verify the task was not executed yet
// ....
// let the test thread sleep and give the task some time to run
Thread.Sleep(2000);
// assert
_queues.Verify(x => x.Publish(It.Is<JctRestartViaSmsValidate>(y => y.Imei == imei)));
}

Unenviable duplication of code in C#

I have the following simple method in C#:
private static void ExtendTaskInternal<U>(
ref U task_to_update, U replace, Action a) where U : Task
{
var current = Interlocked.Exchange(ref task_to_update, replace);
if (current == null)
Task.Run(a);
else
current.AppendAction(a);
}
This is used for the following methods:
//A Task can only run once. But sometimes we wanted to have a reference to some
//tasks that can be restarted. Of cause, in this case "restart" a task means
//replace the reference with a new one. To safely do so we have to ensure a
//lot of things:
//
// * Would the referee be null?
// * Is it still running?
// * The replacement of the task must be atomic
//
//This method can help solving the above issues. If `task_to_update` is null,
//a new Task will be created to replace it. If it is already there, a new Task
//will be created as its continuation, which will only run when the previous
//one finishes.
//
//This is looks like a async mutex, since if you assume `ExtendTask` is the only
//function in your code that updates `task_to_update`, the delegates you pass to
//it runs sequentially. But the difference is that since you have a reference to
//a Task, you can attach continuations that receive notification of lock
//releases.
public static Task<T> ExtendTask<T>(ref Task<T> task_to_update, Func<T> func)
{
var next_ts = new TaskCompletionSource<T>();
ExtendTaskInternal(ref task_to_update, next_ts.Task,
() => next_ts.SetResult(func()));
return next_ts.Task;
}
If you want to do something but only after something else have already been done, this is useful.
Now, this version can only used to replace a Task<T>, not a Task since ref variables are invariant. So if you want it to work for Task as well you have to duplicate the code:
public static Task<T> ExtendTask<T>(ref Task task_to_update, Func<T> func)
{
var next_ts = new TaskCompletionSource<T>();
ExtendTaskInternal(ref task_to_update, next_ts.Task,
() => next_ts.SetResult(func()));
return next_ts.Task;
}
And so you can implement another version that works on Actions.
public static Task ExtendTask(ref Task task_to_update, Action a)
{
return ExtendTask(ref task_to_update, () =>
{
a();
return true;
});
}
So far so good. But I don't like the first and the second version of the ExtendTask, since the body looks exactly the same.
Are there any way to eliminate the duplication?
Background
People ask why not use ContinueWith.
First, notice that AppendAction is just a wrapper function (from Microsoft.VisualStudio.Threading) of ContinueWith so this code is already using it indirectly.
Second, What I did differently here is that I have a reference to update, so this is another wrapper function to ContinueWith, the purpose of those functions is to make it easier to use in some scenarios.
I provide the following concrete example (untested) to illustrate the usage of those methods.
public class Cat {
private Task miuTask = null;
//you have to finish a miu to start another...
private void DoMiu(){
//... do what ever required to "miu".
}
public Task MiuAsync(){
return MyTaskExtension.ExtendTask(ref miuTask, DoMiu);
}
public void RegisterMiuListener(Action whenMiued){
var current = miuTask;
if(current==null) current = TplExtensions.CompletedTask();
current.AppendAction(whenMiued);
}
}

async / await - am I correctly running these methods in parallel?

I have an abstract class called VehicleInfoFetcher which returns information asynchronously from a WebClient via this method:
public override async Task<DTOrealtimeinfo> getVehicleInfo(string stopID);
I'd like to combine the results of two separate instances of this class, running each in parallel before combining the results. This is done within a third class, CombinedVehicleInfoFetcher (also itself a subclass of VehicleInfoFetcher)
Here's my code - but I'm not quite convinced that it's running the tasks in parallel; am I doing it right? Could it be optimized?
public class CombinedVehicleInfoFetcher : VehicleInfoFetcher
{
public HashSet<VehicleInfoFetcher> VehicleInfoFetchers { get; set; }
public override async Task<DTOrealtimeinfo> getVehicleInfo(string stopID)
{
// Create a list of parallel tasks to run
var resultTasks = new List<Task<DTOrealtimeinfo>>();
foreach (VehicleInfoFetcher fetcher in VehicleInfoFetchers)
resultTasks.Add(fetcher.getVehicleInfo(stopID, stopID2, timePointLocal));
// run each task
foreach (var task in resultTasks)
await task;
// Wait for all the results to come in
await Task.WhenAll(resultTasks.ToArray());
// combine the results
var allRealtimeResults = new List<DTOrealtimeinfo>( resultTasks.Select(t => t.Result) );
return combineTaskResults(allRealtimeResults);
}
DTOrealtimeinfo combineTaskResults(List<DTOrealtimeinfo> realtimeResults)
{
// ...
return rtInfoOutput;
}
}
Edit
Some very helpful answers, here is a re-written example to aid discussion with usr below:
public override async Task<object> combineResults()
{
// Create a list of parallel tasks to run
var resultTasks= new List<object>();
foreach (AnotherClass cls in this.OtherClasses)
resultTasks.Add(cls.getResults() );
// Point A - have the cls.getResults() methods been called yet?
// Wait for all the results to come in
await Task.WhenAll(resultTasks.ToArray());
// combine the results
return new List<object>( resultTasks.Select(t => t.Result) );
}
}
Almost all tasks start out already started. Probably, whatever fetcher.getVehicleInfo returns is already started. So you can remove:
// run each task
foreach (var task in resultTasks)
await task;
Task.WhenAll is faster and has better error behavior (you want all exceptions to be propagated, not just the first you happen to stumble upon).
Also, await does not start a task. It waits for completion. You have to arrange for the tasks to be started separately, but as I said, almost all tasks are already started when you get them. This is best-practice as well.
To help our discussion in the comments:
Task Test1() { return new Task(() => {}); }
Task Test2() { return Task.Factory.StartNew(() => {}); }
Task Test3() { return new FileStream("").ReadAsync(...); }
Task Test4() { return new TaskCompletionSource<object>().Task; }
Does not "run" when returned from the method. Must be started. Bad practice.
Runs when returned. Does not matter what you do with it, it is already running. Not necessary to add it to a list or store it somewhere.
Already runs like (2).
The notion of running does not make sense here. This task will never complete although it cannot be explicitly started.

Unit testing code that uses Task.Factory.StartNew().ContinueWith()

so I have some code
Task.Factory.StartNew(() => this.listener.Start()).ContinueWith(
(task) =>
{
if (task.IsCompleted)
{
this.status = WorkerStatus.Started;
this.RaiseStatusChanged();
this.LogInformationMessage("Worker Started.");
}
});
When I am testing I am mocking all the dependant objects (namley this.listener.Start()). the problem is that the test finishes executing before ContinueWith can be called. When I debug it gets called fine due to the extra delay of me stepping through code.
so how can I - from the test code in a different assembly - ensure that the code is run before my test hits its asserts?
I could just use Thread.Sleep ... but this seems like a really hacky way of doing it.
I guess I am looking for the Task version of Thread.Join.
Consider the following:
public class SomeClass
{
public void Foo()
{
var a = new Random().Next();
}
}
public class MyUnitTest
{
public void MyTestMethod()
{
var target = new SomeClass();
target.Foo(); // What to assert, what is the result?..
}
}
What is the value assigned to a? You cannot tell, unless the result is returned outside the method Foo() (as the return value, a public property, an event, etc.).
The process of "coordinating the actions of threads for a predictable outcome" is called Synchronization.
One of the easiest solutions in your case might be to return the instance of Task class and the use its Wait() method:
var task = Task.Factory.StartNew(() => Method1())
.ContinueWith(() => Method2());
No need to wait for the first task, because ContinueWith() creates a continuation that executes asynchronously when the target Task completes (MSDN):
task.Wait();
I don't think there is an easy-yet-practical way of doing this. Ran into the same problem myself just now and Thread.Sleep(X) is by far the simplest (if not elegant) way of getting around the problem.
The only other solution that I considered is hiding the Task.Factory.StartNew() call behind an interface that you can mock from your test thus removing the actual execution of the task entirely in the test scenario (but still have an expectation that the interface method will be called. For example:
public interface ITaskWrapper
{
void TaskMethod();
}
And your concrete implementation:
public class MyTask : ITaskWrapper
{
public void TaskMethod()
{
Task.Factory.StartNew(() => DoSomeWork());
}
}
Then just mock ITaskWrapper in your test method and set an expectation on TaskMethod being called.
If there's any way for you to be notified of when the processing has ended (can you add a handler for that StatusChanged event?), use a ManualResetEvent and wait on it with a reasonable timeout. If the timeout expired fail the test, otherwise go on and perform your assertions.
E.g.
var waitHandle = new ManualResetEvent(false);
sut.StatusChanged += (s, e) => waitHandle.Set();
sut.DoStuff();
Assert.IsTrue(waitHandle.WaitOne(someTimeout), "timeout expired");
// do asserts here
The continuation task will still run regardless of whether the initial task completed before the ContinueWith() call or not. I double checked this with the following:
// Task immediately exits
var task = Task.Factory.StartNew(() => { });
Thread.Sleep(100);
// Continuation on already-completed task
task.ContinueWith(t => { MessageBox.Show("!"); });
Debug further. Maybe your task is failing.
When dealing with asynchronous processes during code under test that use Reactive Extensions, one approach is to use a TestScheduler. The TestScheduler can be moved forward in time, drained of all shceduled tasks, etc. So your code under test can take an IScheduler, which you provide a TestScheduler instance for. Then your test can manipulate time without needing to actually sleep, wait or synchronize. An improvement on this approach is Lee Campbell's ISchedulerProvider approach.
If you use Observable.Start instead of Task.Factory.StartNew in your code, you can then use your TestScheduler in the unit test to push through all the scheduled tasks.
For example, your code under test could look something like this:
//Task.Factory.StartNew(() => DoSomething())
// .ContinueWith(t => DoSomethingElse())
Observable.Start(() => DoSomething(), schedulerProvider.ThreadPool)
.ToTask()
.ContinueWith(t => DoSomethingElse())
and in your unit test:
// ... test code to execute the code under test
// run the tasks on the ThreadPool scheduler
testSchedulers.ThreadPool.Start();
// assertion code can now run

Categories