I'm trying to get a call to WriteAsync mocked on a mockHttpResponse and I can't figure out the syntax to use.
var responseMock = new Mock<HttpResponse>();
responseMock.Setup(x => x.WriteAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()));
ctx.Setup(x => x.Response).Returns(responseMock.Object);
The test bombs with the following error:
System.NotSupportedException : Invalid setup on an extension method: x
=> x.WriteAsync(It.IsAny(), It.IsAny())
Ultimately I want to verify the correct string has been written to the response.
How to correctly set this up?
Here's a solution that seems to work in .NET Core 3.1, for completeness:
const string expectedResponseText = "I see your schwartz is as big as mine!";
DefaultHttpContext httpContext = new DefaultHttpContext();
httpContext.Response.Body = new MemoryStream();
// Whatever your test needs to do
httpContext.Response.Body.Position = 0;
using (StreamReader streamReader = new StreamReader(httpContext.Response.Body))
{
string actualResponseText = await streamReader.ReadToEndAsync();
Assert.Equal(expectedResponseText, actualResponseText);
}
Moq cannot Setup extension methods. If you know what the extension method accesses then some cases you can mock a safe path through the extension method.
WriteAsync(HttpResponse, String, CancellationToken)
Writes the given text to the response body. UTF-8 encoding will be used.
directly accesses the HttpResponse.Body.WriteAsync where Body is a Stream via the following overload
/// <summary>
/// Writes the given text to the response body using the given encoding.
/// </summary>
/// <param name="response">The <see cref="HttpResponse"/>.</param>
/// <param name="text">The text to write to the response.</param>
/// <param name="encoding">The encoding to use.</param>
/// <param name="cancellationToken">Notifies when request operations should be cancelled.</param>
/// <returns>A task that represents the completion of the write operation.</returns>
public static Task WriteAsync(this HttpResponse response, string text, Encoding encoding, CancellationToken cancellationToken = default(CancellationToken))
{
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
if (text == null)
{
throw new ArgumentNullException(nameof(text));
}
if (encoding == null)
{
throw new ArgumentNullException(nameof(encoding));
}
byte[] data = encoding.GetBytes(text);
return response.Body.WriteAsync(data, 0, data.Length, cancellationToken);
}
This means you would need mock response.Body.WriteAsync
//Arrange
var expected = "Hello World";
string actual = null;
var responseMock = new Mock<HttpResponse>();
responseMock
.Setup(_ => _.Body.WriteAsync(It.IsAny<byte[]>(),It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Callback((byte[] data, int offset, int length, CancellationToken token)=> {
if(length > 0)
actual = Encoding.UTF8.GetString(data);
})
.ReturnsAsync();
//...code removed for brevity
//...
Assert.AreEqual(expected, actual);
The callback was used to capture the arguments passed to the mocked member. Its value was stored in a variable to be asserted later in the test.
Related
I want to ReplyAsync lyrics for a particular song so I used the unirest-net api. Here's how I use the command:
/// <summary> The lyric generating task </summary>
/// <returns> The <see cref="Task"/> </returns>
[Command("lyric")]
public async Task LyricTask()
{
Task<HttpResponse<string>> response = Unirest.get("https://musixmatchcom-musixmatch.p.mashape.com/wsr/1.1/matcher.lyrics.get?q_artist=imagine+dragons&q_track=Battle+cry")
.header("X-Mashape-Key", "secret_key")
.header("Accept", "application/json")
.asJsonAsync<string>();
await this.ReplyAsync(response.ToString());
}
But I get the output System.Threading.Tasks.Task1[unirest_net.http.HttpResponse1[System.String]] always. What am i doing wrong and how do i make it work?
I haven't used HttpResponse before but after looking at the documentation, I think you need to call Flush() instead of ToString().
From the Flush() documentation:
Sends all currently buffered output to the client.
Edit: You have to call Flush() from the Result property of the Task<HttpResult> object:
await this.ReplyAsync(response.Result.Flush());
I'm using the swagger-codegen to generate c# client, however noticing that the sortParamsByRequiredFlag is not applied to model generation.
For example, here is a sample config file:
{
"packageVersion" : "1.0.0",
"sortParamsByRequiredFlag": true,
"optionalProjectFile" : false
}
Here is the generated truncated code for model's constructor:
/// <summary>
/// Initializes a new instance of the <see cref="V2alpha1CronJobSpec" /> class.
/// </summary>
/// <param name="ConcurrencyPolicy">Specifies how to treat concurrent executions of a Job. Defaults to Allow..</param>
/// <param name="FailedJobsHistoryLimit">The number of failed finished jobs to retain. This is a pointer to distinguish between explicit zero and not specified..</param>
/// <param name="JobTemplate">Specifies the job that will be created when executing a CronJob. (required).</param>
/// <param name="Schedule">The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. (required).</param>
/// <param name="StartingDeadlineSeconds">Optional deadline in seconds for starting the job if it misses scheduled time for any reason. Missed jobs executions will be counted as failed ones..</param>
/// <param name="SuccessfulJobsHistoryLimit">The number of successful finished jobs to retain. This is a pointer to distinguish between explicit zero and not specified..</param>
/// <param name="Suspend">This flag tells the controller to suspend subsequent executions, it does not apply to already started executions. Defaults to false..</param>
public V2alpha1CronJobSpec(string ConcurrencyPolicy = default(string), int? FailedJobsHistoryLimit = default(int?), V2alpha1JobTemplateSpec JobTemplate = default(V2alpha1JobTemplateSpec), string Schedule = default(string), long? StartingDeadlineSeconds = default(long?), int? SuccessfulJobsHistoryLimit = default(int?), bool? Suspend = default(bool?))
{
// to ensure "JobTemplate" is required (not null)
if (JobTemplate == null)
{
throw new InvalidDataException("JobTemplate is a required property for V2alpha1CronJobSpec and cannot be null");
}
else
{
this.JobTemplate = JobTemplate;
}
// to ensure "Schedule" is required (not null)
if (Schedule == null)
{
throw new InvalidDataException("Schedule is a required property for V2alpha1CronJobSpec and cannot be null");
}
else
{
this.Schedule = Schedule;
}
this.ConcurrencyPolicy = ConcurrencyPolicy;
this.FailedJobsHistoryLimit = FailedJobsHistoryLimit;
this.StartingDeadlineSeconds = StartingDeadlineSeconds;
this.SuccessfulJobsHistoryLimit = SuccessfulJobsHistoryLimit;
this.Suspend = Suspend;
}
As you can see from the swagger spec, JobTemplate, Schedule are required parameters. However, the params in the constructor are sorted alphabetically.
I've been sorting through the swagger-codegen code base and I think the sortParamsByRequiredFlag only applies to API generated methods.
Is this by design? I'm not sure if I'm missing some config that I should be setting?
Here is the GitHub issue I opened but haven't heard anything on it.
This is not an answer to why Swagger is not generating the correct code, but an answer to your problem on GitHub.
There is absolutely no need for you to use this:
var jobSpec = new V2alpha1CronJobSpec(null, null, new V2alpha1JobTemplateSpec(), "stringValue", null, ...);
You can just use this (called named parameters):
var jobSpec = new V2alpha1CronJobSpec(JobTemplate: new V2alpha1JobTemplateSpec(), Schedule: "stringValue");
For c# client this flag is ignored in the main codebase.
You can create your own extension and override fromModel method to sort the properties.
Have a look at below code on how to do it.
#Override
public CodegenModel fromModel(String name, Model model, Map<String, Model> allDefinitions) {
CodegenModel codegenModel = super.fromModel(name, model, allDefinitions);
if (sortParamsByRequiredFlag)
{
Collections.sort(codegenModel.readWriteVars, new Comparator<CodegenProperty>() {
#Override
public int compare(CodegenProperty one, CodegenProperty another) {
if (one.required == another.required) return 0;
else if (one.required) return -1;
else return 1;
}
});
System.out.println("***Sorting based on required params in model....***");
}
return codegenModel;
}
I am new to unit testing and I am having some difficulties with unit testing background workers.
Method being tested
[HandleExceptions(LoggerName)]
public override IHttpActionResult ValidateToken(string auth_token)
{
var response = new HttpResponseMessage();
var userInfo = this._userInfoService.CreateUserInfoModel(auth_token);
response.StatusCode = HttpStatusCode.BadRequest;
if (userInfo.Status.IsSuccess)
{
//-- I want to test if the following worker is calling RunPlayerDetailsWorkflow method exactly once.
_taskScheduler.QueueBackgroundWorkItem(task =>
this._playerDetailsService.RunPlayerDetailsWorkflow(userInfo.UserID, userInfo.ServerID));
response.StatusCode = HttpStatusCode.OK;
}
response.Content = new StringContent(_userInfoService.ParseUserInfo(userInfo));
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/plain");
return this.ResponseMessage(response);
}
Test method
[Test]
public void TestValidateToken_Should_Call_PlayerDetailsService_RunPlayerDetailsWorkflow_Exactly_Once()
{
// Arrange
var userInfoModel = AdminControllerHelpers.CreateUserInfoModel();
userInfoModel.Status = ETIStatus.CreateSuccess();
_mockUserInfoService
.Setup(m => m.CreateUserInfoModel(It.IsAny<string>()))
.Returns(userInfoModel);
_mockUserInfoService
.Setup(s => s.ParseUserInfo(userInfoModel))
.Returns(_randomString);
_mockPlayerService
.Setup(m => m.RunPlayerDetailsWorkflow(userInfoModel.UserID, userInfoModel.ServerID));
//-- this is where I got the error.
_mockTaskScheduler
.Setup(t => t.QueueBackgroundWorkItem(
obj => _mockPlayerService.Object.RunPlayerDetailsWorkflow(userInfoModel.UserID,
userInfoModel.ServerID)));
// Act
_controller.ValidateToken(It.IsAny<string>());
// Assert
_mockPlayerService
.Verify(m => m.RunPlayerDetailsWorkflow(userInfoModel.UserID, userInfoModel.ServerID), Times.Exactly(1));
}
Even though I am setting up everything correctly (at least to me) I get following error:
An exception of type 'System.ArgumentException' occurred in System.Core.dll but was not handled in user code. Additional information: Argument types do not match
Task Scheduler interface which I am mocking
public interface ITaskScheduler
{
/// <summary>
/// Schedules a task which can run in the background, independent of any request.
/// </summary>
/// <param name="workItem"> A unit of execution.</param>
void QueueBackgroundWorkItem(Action<CancellationToken> workItem);
}
Change the setup to expect an action and then use the callback to execute the mock
_mockTaskScheduler
.Setup(_ => _.QueueBackgroundWorkItem(It.IsAny<Action<CanellationToken>>()))
.Callback((Action<CancellationToken> action) => action(CancellationToken.None));
Also when exercising the method under test, no need to use It.IsAny<>(). Just pass a value.
// Act
_controller.ValidateToken(String.Empty);
// Assert
_mockPlayerService
.Verify(m => m.RunPlayerDetailsWorkflow(userInfoModel.UserID, userInfoModel.ServerID), Times.Exactly(1));
Should be able to exercise the test as expected now.
I am using this library as wrapper for Mailchimp API v3.0
Now I am having issue to catch Exceptions from methods of this library.
The methods are throwing MailChimpException which is extended from Exception.
The thing is if I run it from Console APP then I am able to catch exception, but when I run it from my Web project, I cannot catch Exception and the code just stuck when exception ocurred.
Here is example of My call:
public bool ListSubscribe(string emailAddress, string apiKey, string listId)
{
try
{
var api = new MailChimpManager(apiKey);
var profile = new Member();
profile.EmailAddress = emailAddress;
var output = api.Members.AddOrUpdateAsync(listId, profile).Result;
return true;
}
catch (Exception ex)
{
logger.Error("An error ocurred in trying to Subscribe to Mailchimp list. {0}", ex.Message);
return false;
}
}
And here is the method from the library which I call:
namespace MailChimp.Net.Logic
{
/// <summary>
/// The member logic.
/// </summary>
internal class MemberLogic : BaseLogic, IMemberLogic
{
private const string BaseUrl = "lists";
/// <summary>
/// Initializes a new instance of the <see cref="MemberLogic"/> class.
/// </summary>
/// <param name="apiKey">
/// The api key.
/// </param>
public MemberLogic(string apiKey)
: base(apiKey)
{
}
/// <summary>
/// The add or update async.
/// </summary>
/// <param name="listId">
/// The list id.
/// </param>
/// <param name="member">
/// The member.
/// </param>
/// <returns>
/// The <see cref="Task"/>.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref>
/// <name>uriString</name>
/// </paramref>
/// is null. </exception>
/// <exception cref="UriFormatException">In the .NET for Windows Store apps or the Portable Class Library, catch the base class exception, <see cref="T:System.FormatException" />, instead.<paramref name="uriString" /> is empty.-or- The scheme specified in <paramref name="uriString" /> is not correctly formed. See <see cref="M:System.Uri.CheckSchemeName(System.String)" />.-or- <paramref name="uriString" /> contains too many slashes.-or- The password specified in <paramref name="uriString" /> is not valid.-or- The host name specified in <paramref name="uriString" /> is not valid.-or- The file name specified in <paramref name="uriString" /> is not valid. -or- The user name specified in <paramref name="uriString" /> is not valid.-or- The host or authority name specified in <paramref name="uriString" /> cannot be terminated by backslashes.-or- The port number specified in <paramref name="uriString" /> is not valid or cannot be parsed.-or- The length of <paramref name="uriString" /> exceeds 65519 characters.-or- The length of the scheme specified in <paramref name="uriString" /> exceeds 1023 characters.-or- There is an invalid character sequence in <paramref name="uriString" />.-or- The MS-DOS path specified in <paramref name="uriString" /> must start with c:\\.</exception>
/// <exception cref="MailChimpException">
/// Custom Mail Chimp Exception
/// </exception>
/// <exception cref="TargetInvocationException">The algorithm was used with Federal Information Processing Standards (FIPS) mode enabled, but is not FIPS compatible.</exception>
/// <exception cref="ObjectDisposedException">
/// The object has already been disposed.
/// </exception>
/// <exception cref="EncoderFallbackException">
/// A fallback occurred (see Character Encoding in the .NET Framework for complete explanation)-and-<see cref="P:System.Text.Encoding.EncoderFallback"/> is set to <see cref="T:System.Text.EncoderExceptionFallback"/>.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// Enlarging the value of this instance would exceed <see cref="P:System.Text.StringBuilder.MaxCapacity"/>.
/// </exception>
/// <exception cref="FormatException">
/// <paramref>
/// <name>format</name>
/// </paramref>
/// includes an unsupported specifier. Supported format specifiers are listed in the Remarks section.
/// </exception>
public async Task<Member> AddOrUpdateAsync(string listId, Member member)
{
using (var client = this.CreateMailClient($"{BaseUrl}/"))
{
var response =
await
client.PutAsJsonAsync($"{listId}/members/{this.Hash(member.EmailAddress.ToLower())}", member, null).ConfigureAwait(false);
await response.EnsureSuccessMailChimpAsync().ConfigureAwait(false);
return await response.Content.ReadAsAsync<Member>().ConfigureAwait(false);
}
}
}
}
Does anyone has idea why I am able to trigger exception in Console APP but not in Web project. Any suggestion what to check?
UPDATE:
If I change my code to use Task.Run like:
var task = Task.Run(async () => { await api.Members.AddOrUpdateAsync(listId, profile); });
task.Wait();
SO:
public bool ListSubscribe(string emailAddress, string apiKey, string listId)
{
try
{
var api = new MailChimpManager(apiKey);
var profile = new Member();
profile.EmailAddress = emailAddress;
var task = Task.Run(async () => { await api.Members.AddOrUpdateAsync(listId, profile); });
task.Wait();
return true;
}
catch (Exception ex)
{
logger.Error("An error ocurred in trying to Subscribe to Mailchimp list. {0}", ex.Message);
return false;
}
}
Then I catch Exception in catch block, but then How I can get result from task when everything is fine???
The proper solution is to make this "async all the way", as I describe in my MSDN article on async best practices. The problem with calling Result directly is that it causes a deadlock (as I describe on my blog post Don't block on async code). (In your case, a secondary problem from Result is that it will wrap MailChimpException within an AggregateException, making error handling more awkward).
Wrapping the call to Task.Run and then calling Result is a hack that decreases the scalability of your web service (and doesn't take care of the secondary problem of exception wrapping, either). If you don't want to use asynchronous code, then use a synchronous API. Since this is an I/O operation, the most natural approach is asynchronous code, which would look like this:
public async Task<bool> ListSubscribeAsync(string emailAddress, string apiKey, string listId)
{
try
{
var api = new MailChimpManager(apiKey);
var profile = new Member();
profile.EmailAddress = emailAddress;
var output = await api.Members.AddOrUpdateAsync(listId, profile);
return true;
}
catch (Exception ex)
{
logger.Error("An error ocurred in trying to Subscribe to Mailchimp list. {0}", ex.Message);
return false;
}
}
This is an answer to the updated question.
You can get the result simply by calling Task.Result on the task which may throw an exception. You lambda expression has to return the called method's Task<T> result using return await. Otherwise the result of the the whole expression will be a non-generic task, which represents an operation that does not return a value and doesn't have a Result member.
static void Main(string[] args)
{
try
{
var task = Task.Run(async () => { return await AsyncMethod(); });
Console.WriteLine("Result: " + task.Result);
}
catch
{
Console.WriteLine("Exception caught!");
}
Console.ReadKey();
}
static async Task<string> AsyncMethod()
{
throw new ArgumentException();
var result = await Task.Run(() => { return "Result from AsyncMethod"; });
return result;
}
Notice that I've removed the call to Task.Wait, because it is unnecessary when you also fetch the result.
Quote from MSDN's Task<TResult>.Result reference page:
Accessing the property's get accessor blocks the calling thread until
the asynchronous operation is complete; it is equivalent to calling
the Wait method.
The code snippet will display the text from catch block. If you remove the first line from AsyncMethod, you will get the result displayed.
This: https://blogs.msdn.microsoft.com/ptorr/2014/12/10/async-exceptions-in-c/
To cut a long story short, you never access the result of your async method, and never see the exception. If you dont want to make the parent method async, then you need to explicitly get the value of the .Result property from the task object returned by AddOrUpdateAsync method.
Edit: Context: I write a Silverlight application that accesses a Dynamics CRM 2011 via the Soap Service. I implemented a service for doing this. I want to write a unit test for this service now.
I want to write a unit test for the following method:
public async Task<List<string>> GetAttributeNamesOfEntity(string entityName)
{
// build request
OrganizationRequest request = new OrganizationRequest
{
RequestName = "RetrieveEntity",
Parameters = new ParameterCollection
{
new XrmSoap.KeyValuePair<string, object>()
{
Key = "EntityFilters",
Value = EntityFilters.Attributes
},
new XrmSoap.KeyValuePair<string, object>()
{
Key = "RetrieveAsIfPublished",
Value = true
},
new XrmSoap.KeyValuePair<string, object>()
{
Key = "LogicalName",
Value = "avobase_tradeorder"
},
new XrmSoap.KeyValuePair<string, object>()
{
Key = "MetadataId",
Value = new Guid("00000000-0000-0000-0000-000000000000")
}
}
};
// fire request
IAsyncResult result = OrganizationService.BeginExecute(request, null, OrganizationService);
// wait for response
TaskFactory<OrganizationResponse> tf = new TaskFactory<OrganizationResponse>();
OrganizationResponse response = await tf.FromAsync(result, iar => OrganizationService.EndExecute(result));
// parse response
EntityMetadata entities = (EntityMetadata)response["EntityMetadata"];
return entities.Attributes.Select(attr => attr.LogicalName).ToList();
}
My first approach was to call the actual CRM. This failed because I cannot authenticate. I asked a question about this here. My second approach is to mock the organization service and run the method against my the mock. Like this:
private bool _callbackCalled = false;
private SilverlightDataService _service;
private List<string> names;
[TestInitialize]
public void SetUp()
{
IOrganizationService organizationService = A.Fake<IOrganizationService>();
_service = new SilverlightDataService {OrganizationService = organizationService};
IAsyncResult result = A.Fake<IAsyncResult>();
A.CallTo(organizationService.BeginExecute(A<OrganizationRequest>.Ignored, A<AsyncCallback>.Ignored,
A<object>.Ignored)).WithReturnType<IAsyncResult>().Returns(result);
EntityMetadata entities = new EntityMetadata();
AttributeMetadata meta = new AttributeMetadata();
meta.LogicalName = "foobar";
entities.Attributes = new ObservableCollection<AttributeMetadata>();
entities.Attributes.Add(meta);
OrganizationResponse response = new OrganizationResponse();
response.Results = new ParameterCollection();
response["EntityMetadata"] = entities;
A.CallTo(() => result.IsCompleted).Returns(true);
A.CallTo(result.AsyncState).WithReturnType<object>().Returns(response);
A.CallTo(organizationService.EndExecute(result)).WithReturnType<OrganizationResponse>().Returns(response);
}
[TestMethod]
[Asynchronous]
public async void TestGetAttributeNamesOfEntity()
{
TaskFactory<List<string>> tf = new TaskFactory<List<string>>();
names = await tf.FromAsync(_service.GetAttributeNamesOfEntity("avobase_tradeorder"), CallbackFunction);
Assert.IsTrue(_callbackCalled);
Assert.IsNotNull(names);
}
/// <summary>
/// is used as callback for the method GetAttributeNamesOfEntity
/// </summary>
/// <param name="result"></param>
/// <returns></returns>
private List<string> CallbackFunction(IAsyncResult result)
{
_callbackCalled = true;
names = (List<string>) result.AsyncState;
return null;
}
I use FakeItEasy for mocking. When I execute this test (with ReSharper and AgUnit) it waits forever. I also tried this approach, but it did not work.
1) My assumption is that my mocks dont tell the await magic that the task is actually done. Why does the await never end? What do I have to do to fix this?
2) Is there maybe a better approach to do this?
Edit: I am using: VS 2012 Premium, ReSharper 7.1, Silverlight 5, AgUnit 0.7, .Net 4.5, FakeItEasy 1.13.1 installed via NuGet.