I want to test the Save() method which is residing inside the Contributor class. This method, in turn, opens a dialog which, when finished loading, actions an event that triggers the method that I want to mock - PushToPortal.
internal class Contributor
{
private readonly IEntryPointWrapper _entryPoint;
private readonly ISyncDialog _dialog;
public event Action OnPushToWeldCompleted;
internal void Save(Document document)
{
...
void Ptp() => PushToPortal(information);
_dialog.ShowDialog(Ptp);
}
}
When ShowDialog is called, OnLoadingCompleted += Ptp; is invoked.
The problem begins just here. PushToPortal looks like this:
internal virtual void PushToPortal(Information information)
{
var pushToPortal = Task.Run(() => {
var result = _entryPoint.ProcessElements(information);
});
pushToPortal.ContinueWith(task => OnPushToPortalCompleted?.Invoke(), TaskScheduler.FromCurrentSynchronizationContext());
}
Basically, when running the tests, they continue running while the async method is still processing and when asserting, the callback does not retrieve the needed information unless I use Thread.Sleep, which is not a solution that I'd like.
The working solution would be:
_entryPointMock.Setup(epm => epm.ProcessElements(It.IsAny<Information>()))
.Callback<WeldInformation>(information => actualWeldInformation = information)
.Returns(new InfoResult { Status = Status.Succeed, InfoCount = 1 });
Thread.Sleep(5000);
_contributor.Save(document);
I tried mocking the PushToPortal method and use a Callback to retrieve its arguments, without going into ProcessElements, but it does not seem to do anything at all.
_entryPointMock.Setup(epm => epm.ProcessElements(It.IsAny<Information>()))
.Returns(new InfoResult { Status = Status.Succeed, InfoCount = 1 });
var mock = new Mock<Contributor>();
mock.CallBase = true;
mock.Setup(x => x.PushToPortal(It.IsAny<Information>()))
.Callback<Information>(information => actualInformation = information);
_contributor.Save(document);
Therefore, how can I mock PushToPortal properly such that I retrieve the information, without actually entering the new thread which processes the elements? I am looking for a solution which does not imply changing the current code very much - an example which I thought of (and would not like to implement) would've been breaking down the functionality and returning a Task.
Related
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>();
}
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)));
}
I am currently using Moq to help with my unit testing however I ran in to an issue that I do not know how to resolve.
For example, say I would like to validate that CancellationToken.ThrowIfCancellationRequested() was called once per Upload( call
public UploadEngine(IUploader uploader)
{
_uploader = uploader;
}
public void PerformUpload(CancellationToken token)
{
token.ThrowIfCancellationRequested();
_uploader.Upload(token, "Foo");
token.ThrowIfCancellationRequested();
_uploader.Upload(token, "Bar");
}
If token was a reference type I normally would do something like
[TestMethod()]
public void PerformUploadTest()
{
var uploader = new Mock<IUploader>();
var token = new Mock<CancellationToken>();
int callCount = 0;
uploader.Setup(a => a.Upload(token.Object, It.IsAny<string>())).Callback(() => callCount++);
token.Setup(a => a.ThrowIfCancellationRequested());
var engine = new UploadEngine(uploader.Object);
engine.PerformUpload(token.Object);
token.Verify(a => a.ThrowIfCancellationRequested(), Times.Exactly(callCount));
}
However from what I can tell Moq does not support value types. What would be the correct way to test this, or is there no way to do what I want through Moq without boxing the CancellationToken inside a container first to be passed in to PerformUpload(?
You've probably moved on from this, but it occurred to me that what you're trying to test doesn't really seem to make sense. Testing that ThrowIfCancellationRequested is called the same number of times as Upload does nothing to ensure that they have been called in the right sequence, which I'm assuming would actually be relevant in this case. You wouldn't want code like this to pass, but I'm pretty sure it would:
_uploader.Upload(token, "Foo");
token.ThrowIfCancellationRequested();
token.ThrowIfCancellationRequested();
_uploader.Upload(token, "Bar");
As has been said in the comments, the easiest way to get round this would be to push the token.ThrowIfCancellationRequested call into the Upload call. Assuming that for whatever reason this isn't possible, I might take the following approach to testing your scenario.
Firstly, I'd encapsulate the functionality of checking to see if the cancellation had been requested and if not, calling an action into something testable. At first thought, this might look like this:
public interface IActionRunner {
void ExecIfNotCancelled(CancellationToken token, Action action);
}
public class ActionRunner : IActionRunner{
public void ExecIfNotCancelled(CancellationToken token, Action action) {
token.ThrowIfCancellationRequested();
action();
}
}
This can be fairly trivially tested with two tests. One to check that action is called if the token isn't cancelled and one to validate that it isn't if it is cancelled. These tests would look like:
[TestMethod]
public void TestActionRunnerExecutesAction() {
bool run = false;
var runner = new ActionRunner();
var token = new CancellationToken();
runner.ExecIfNotCancelled(token, () => run = true);
// Validate action has been executed
Assert.AreEqual(true, run);
}
[TestMethod]
public void TestActionRunnerDoesNotExecuteIfCancelled() {
bool run = false;
var runner = new ActionRunner();
var token = new CancellationToken(true);
try {
runner.ExecIfNotCancelled(token, () => run = true);
Assert.Fail("Exception not thrown");
}
catch (OperationCanceledException) {
// Swallow only the expected exception
}
// Validate action hasn't been executed
Assert.AreEqual(false, run);
}
I would then inject the IActionRunner into the UploadEngine and validate that it was being called correctly. So, your PerformUpload method would change to:
public void PerformUpload(CancellationToken token) {
_actionRunner.ExecIfNotCancelled(token, () => _uploader.Upload(token, "Foo"));
_actionRunner.ExecIfNotCancelled(token, () => _uploader.Upload(token, "Bar"));
}
You can then write a pair of tests to validate PerformUpload. The first checks that if an ActionRunner mock has been setup to execute the supplied action then Upload is called at least once. The second test validates that if the ActionRunner mock has been setup to ignore the action, then Upload isn't called. This essentially ensures that all Upload calls in the method are done via the ActionRunner. These tests look like this:
[TestMethod]
public void TestUploadCallsMadeThroughActionRunner() {
var uploader = new Mock<IUploader>();
var runner = new Mock<IActionRunner>();
var token = new CancellationToken();
int callCount = 0;
uploader.Setup(a => a.Upload(token, It.IsAny<string>())).Callback(() => callCount++);
// Use callback to invoke actions supplied to runner
runner.Setup(x => x.ExecIfNotCancelled(token, It.IsAny<Action>()))
.Callback<CancellationToken, Action>((tok,act)=>act());
var engine = new UploadEngine(uploader.Object, runner.Object);
engine.PerformUpload(token);
Assert.IsTrue(callCount > 0);
}
[TestMethod]
public void TestNoUploadCallsMadeThroughWithoutActionRunner() {
var uploader = new Mock<IUploader>();
var runner = new Mock<IActionRunner>();
var token = new CancellationToken();
int callCount = 0;
uploader.Setup(a => a.Upload(token, It.IsAny<string>())).Callback(() => callCount++);
// NOP callback on runner prevents uploader action being run
runner.Setup(x => x.ExecIfNotCancelled(token, It.IsAny<Action>()))
.Callback<CancellationToken, Action>((tok, act) => { });
var engine = new UploadEngine(uploader.Object, runner.Object);
engine.PerformUpload(token);
Assert.AreEqual(0, callCount);
}
There would obviously be other tests that you might want to write for your UploadEngine but they seem out of scope for the current question...
I have the following two methods:
private static void webservicecalls()
{
WebServiceOne();
WebServiceTwo();
}
So how i can call these two methods asynchronously instead of waiting for the first web service call to be complete before calling the second web service?
Update:
Now both WebSericeOne and WebServiceTwo have sub calls such as:
private static void WebServiceOne()
{
CallOne();
CallThree();
}
private static void WebServiceTwo()
{
CallTwo();
CallFour();
}
so i need the call to have the following sequence for the sub calls :-
CallOne();
CallTwo();
CallThree();
CallFour();
Depending on what are you using to implement webserviceone webservicetwo, it is quite likely that the web-service tooling itself will have full async support, so the first thing to do is: check that, and use the async API rather than the sync API.
If, for some reason, that does not exist - you could do something like:
var task = new Task(() => webservice1());
task.Start();
webservice2();
task.Wait();
(obviously you could run both fully async; I'm assuming in the above you just want to run them both in parallel, and "join" at the end)
Re your edit: if the order must be exactly "one","two","three","four", then you can't do them in parallel, so you would have to instead just run the entire thing in the background:
new Task(() => {
webservice1();
webservice2();
}).Start();
which will guarantee the execution order, sacrificing parallelism.
Action one = () => WebServiceOne();
Action two = () => WebServiceTwo();
one.BeginInvoke(null, null);
two.BeginInvoke(null, null);
I like using an Action.
private static void WebServiceCalls()
{
var someState = 3;
Action a1 = () => { WebserviceOne(); };
Action a2 = () => { WebserviceTwo(); };
a1.BeginInvoke(WebServiceCallBack, someState); //see code section below
a2.BeginInvoke(null, null); //fire and forget...
}
This also allows you to create a Callback for handling code:
void WebServiceCallBack(IAsyncResult result)
{
int state = (int)result.AsyncState;
//do more??
}
#Mark brings up a valid point: If you are working with a webservice within Visual studio, you can use the service reference configuration window to create the Channel Proxy class with async syntax. (I.e. with the BeginWebServiceOne and EndWebServiceOne which would preclude the need to create Async wrappers.)
Take a look at the task library here, and the navigation for the sub categories on the left
http://msdn.microsoft.com/en-us/library/dd537609.aspx
Taking my first steps with Rx I am stuck here:
public class DisposableResourceDemo : IDisposable
{
public DisposableResourceDemo() {
Console.WriteLine("DisposableResourceDemo constructor.");
}
public void Dispose() {
Console.WriteLine("DisposableResourceDemo.Dispose()");
}
public void SideEffect() {
Console.WriteLine("DisposableResourceDemo.SideEffect()");
}
}
[Test]
public void ShowBehaviourOfRxUsing()
{
var test = Observable.Using(() =>
{
// This should happen exactly once, independent of number of subscriptions,
// object should be disposed on last subscription disposal or OnCompleted call
return new DisposableResourceDemo();
},
(dr) =>
{
return Observable.Create<string>(
(IObserver<string> observer) =>
{
dr.SideEffect();
var dummySource = Observable.Return<string>("Some Text");
return dummySource.Subscribe(observer);
});
}).Publish().RefCount();
Console.WriteLine("before 1st subscription.");
test.Subscribe(Console.WriteLine, () => Console.WriteLine("OnCompleted in 1st."));
Console.WriteLine("before 2nd subscription.");
test.Subscribe(Console.WriteLine, () => Console.WriteLine("OnCompleted in 2nd."));
}
To my surprise the code above yields
before 1st subscription.
DisposableResourceDemo constructor.
DisposableResourceDemo.SideEffect()
Some Text
OnCompleted in 1st.
DisposableResourceDemo.Dispose()
before 2nd subscription.
--> [happy with missing "Some Text" here]
OnCompleted in 2nd.
--> [unhappy with second instantiation here]
DisposableResourceDemo constructor.
DisposableResourceDemo.SideEffect()
DisposableResourceDemo.Dispose()
Please note that calling Connect() manually after both subscriptions is not what I want here, though then the output is as expected.
I am not totally sure what you are trying to achieve here. It seems that you want to share the observable sequence and its related resources. So the standard ways to do this is with the ConnectableObservable types that you get from .Replay() and .Publish() etc
You say you dont want to use .Connect() and instead you use .RefCount() which is very common. However, your sequence completes. You also are using the Extension method Subscribe(...) which will internally create an Auto detaching observer, i.e. when the sequence completes, it will disconnect.
So my question is, should the internal sequence actually complete?
If the answer is yes, then why would the 2nd subscription get the OnComplete notification...it has happened already, it is in the past. Maybe you do want to replay the OnComplete, in which case maybe .Replay(1) is what you want.
If the answer is no, then you can easily fix this by putting a Concat(Observable.Never<string>()) either before the .Publish() or after the Observable.Return.