I'm doing some unit testing in C# using NUnit and NSubstitute. I have a class called Adapter, which has a method, GetTemplates(), I want to unit test. GetTemplates() uses httpclient, which I have mocked out using an interface.
The call in GetTemplates looks something like:
public async Task<List<Template>> GetTemplates()
{
//Code left out for simplificity.
var response = await _client.GetAsync($"GetTemplates");
if (!response.IsSuccessStatusCode)
{
throw new Exception();
}
}
I want _client.GetAsync to return a HttpResponseMessage with a HttpStatusCode.BadRequest so that I can test if the exception is being thrown.
The test method looks like:
[Test]
public void GetTemplate_ReturnBadRequestHttpMessage_ThrowException()
{
//Arrange.
var httpMessage = new HttpResponseMessage(HttpStatusCode.BadRequest);
_client.GetAsync("").Returns(Task.FromResult(httpMessage));
//Act.
var ex = Assert.ThrowsAsync<Exception>(async () => await _Adapter.GetSigningTemplates());
//Assert.
Assert.IsInstanceOf<Exception>(ex);
}
When the method has run, it returns
System.NullReferenceException: Object reference not set to an instance of an object.
What did I do wrong?
That is because the arrangement of the mocked client does not match what is actually invoked when the test is exercised.
The client expects
var response = await _client.GetAsync($"GetTemplates");
but the setup is for
_client.GetAsync("")
note the different arguments passed. When mocks do not get exactly what was setup they usually return the default value of their return type, which in this case is null.
Change the test to use the expected parameter
_client.GetAsync($"GetTemplates").Returns(Task.FromResult(httpMessage));
Reference Return for specific args
or use an argument matcher
_client.GetAsync(Arg.Any<string>()).Returns(Task.FromResult(httpMessage));
Reference Argument matchers
Related
I have dotnet WebAPI and I'm trying to get a specific behaviour but am constantly getting 415 responses.
I have reproduced this by starting a new webapi project using dotnet new webapi on the command line. From there, I added two things: a new controller, and a model class. In my real project the model class is obviously a bit more complex, with inheritance and methods etc...
Here they are:
[HttpGet("/data")]
public async Task<IActionResult> GetModel(BodyParams input)
{
var response = new { Message = "Hello", value = input.valueOne };
return Ok(response);
}
public class BodyParams {
public bool valueOne { get; set; } = true;
}
My goal is that the user can call https://localhost:7222/data with no headers or body needed at all, and will get the response - BodyParams will be used with the default value of true. Currently, from postman, or from the browser, I get a 415 response.
I've worked through several suggestions on stack and git but nothing seems to be working for me. Specifically, I have tried:
Adding [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] into the controller, but this makes no difference unless I provide an empty {} json object in the body. This is not what I want.
Making BodyParams nullable - again, no change.
Adding .AddControllers(opt => opt.AllowEmptyInputInBodyModelBinding = true)... again, no change.
I Implemented the solution suggested here using the attribute modification in the comment by #HappyGoLucky. Again, this did not give the desired outcome, but it did change the response to : 400 - "The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true."
I tried modifying the solution in (4) to manually set context.HttpContext.Request.Body to an empty json object... but I can't figure out the syntax for this because it need to be a byte array and at that point I feel like I am way over complicating this.
How can I get the controller to use BodyParams with default values in the case that the user provides no body and no headers at all?
You can achieve that using a Minimal API.
app.MapGet("/data",
async (HttpRequest httpRequest) =>
{
var value = true;
if (Equals(httpRequest.GetTypedHeaders().ContentType, MediaTypeHeaderValue.Parse("application/json")))
{
var bodyParams = await httpRequest.ReadFromJsonAsync<BodyParams>();
if (bodyParams is not null) value = bodyParams.ValueOne;
}
var response = new {Message = "Hello", value};
return Results.Ok(response);
});
So, as there doesn't seem to be a more straightforward answer, I have currently gone with the approach number 5) from the OP, and just tweaking the code from there very slightly.
All this does is act as an action which checks the if the user has passed in any body json. If not, then it adds in an empty anonymous type. The behaviour then is to use the default True value from the BodyParams class.
The full code for the action class is:
internal class AllowMissingContentTypeForEmptyBodyConvention : Attribute, IActionModelConvention
{
public void Apply(ActionModel action)
{
action.Filters.Add(new AllowMissingContentTypeForEmptyBodyFilter());
}
private class AllowMissingContentTypeForEmptyBodyFilter : IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
if (!context.HttpContext.Request.HasJsonContentType()
&& (context.HttpContext.Request.ContentLength == default
|| context.HttpContext.Request.ContentLength == 0))
{
context.HttpContext.Request.ContentType = "application/json";
var str = new { };
//convert string to jsontype
var json = JsonConvert.SerializeObject(str);
//modified stream
var requestData = Encoding.UTF8.GetBytes(json);
context.HttpContext.Request.Body = new MemoryStream(requestData);
}
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
// Do nothing
}
}
}
Then you can add this to any of your controllers using [AllowMissingContentTypeForEmptyBodyConvention]
I have an ASP .Net Core 2.2 Web API. In it I am using SignalR. In my hub, I need to save messages to the database, so I am getting an instance of DbContext, and because I am calling the SaveChangesAsync() method of DbContext, I need to make the method async. So, from
public Task SendMessageToAll(string message)
{
return Clients.All.SendAsync("ReceiveMessage", message);
}
I now have
public async Task SendMessageToAll(string message)
{
using (var scope = _serviceProvider.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<DbContext>();
Message newMessage = new Message()
{
Body = message,
Timestamp = DateTime.Now
};
dbContext.Messages.Add(newMessage);
await dbContext.SaveChangesAsync();
}
return Clients.All.SendAsync("ReceiveMessage", message);
}
However, now I'm getting this error:
Since 'ChatHub.SendMessageToAll(string)' is an async method that returns 'Task', a return keyword must not be followed by an object expression. Did you intend to return 'Task'?
Which makes sense, but I'm not sure how to fix it. Any ideas?
Change your code as below:
return Clients.All.SendAsync("ReceiveMessage", message);
await Clients.All.SendAsync("ReceiveMessage", message);
I have a Web API that gets call by this method:
public async Task<Model> AddModel(string token, Model newModel)
{
HttpContent content = await HttpHelper.Request(token, baseUrl, url, newModel, HttpRequestType.POST);
return await content.ReadAsAsync<Model>();
}
The Web API is successfully called and I can add a Model. This is the Web API method that gets called:
[Route("webapi/models/addModel")]
[HttpPost]
public async Task<ModelDto> AddWithoutResetingDefault(ModelDto newModel)
{
ModelService modelService = new ModelService();
return modelService.AddModel(newModel);
}
The problem is after the successful add, it doesn't return to the calling code anymore (I have a breakpoint that doesn't get hit). There are no console errors in the browser, I enclosed in a try-catch the calling code and the called code but there were no exceptions thrown.
Also, after the first click to add, if I try to refresh the browser, it takes a really long time to reload the browser (I don't know if being async has something to do with it).
(I don't know if being async has something to do with it) - Yes it has
your Api method
public async Task<ModelDto> AddWithoutResetingDefault(ModelDto newModel)
{
ModelService modelService = new ModelService();
return modelService.AddModel(newModel);
}
is marked as Async method, but the code inside is Sync. And that is the problem, if your modelService.AddModel(newModel); is async, then do
return await modelService.AddModel(newModel);
if its not then there is no point in making the AddWithoutResetingDefault
method async, hence remove Aysnc and simply do a sync method like
public ModelDto AddWithoutResetingDefault(ModelDto newModel)
{
ModelService modelService = new ModelService();
return modelService.AddModel(newModel);
}
So I'd like to write a new set of evaluators in Share based on the result of some repository webscripts.
The current existing Share evaluators are usable through some XML configuration and are related to Alfresco usual meta-data.
But, I'd like to know how to write my own Java evaluator while re using most of the logic already here (BaseEvaluator).
Suppose I have a repository webscript that returns some JSON like {"result" : "true"}:
How do I access it from my custom Evaluator? Mainly how do I access the proxy URL to alfresco webapp from the Spring context?
Do I need to write my own async call in Java?
Where do I find this JSONObject parameter of the required evaluate method?
thanks for your help
See if this helps. This goes into a class that extends BaseEvaluator. Wire the bean in through Spring, then set the evaluator on your actions.
public boolean evaluate(JSONObject jsonObject) {
boolean result = false;
final RequestContext rc = ThreadLocalRequestContext.getRequestContext();
final String userId = rc.getUserId();
try {
final Connector conn = rc.getServiceRegistry().getConnectorService().getConnector("alfresco", userId, ServletUtil.getSession());
final Response response = conn.call("/someco/example?echo=false");
if (response.getStatus().getCode() == Status.STATUS_OK) {
System.out.println(response.getResponse());
try {
org.json.JSONObject json = new org.json.JSONObject(response.getResponse());
result = Boolean.parseBoolean(((String) json.get("result")));
} catch (JSONException je) {
je.printStackTrace();
return false;
}
} else {
System.out.println("Call failed, code:" + response.getStatus().getCode());
return false;
}
} catch (ConnectorServiceException cse) {
cse.printStackTrace();
return false;
}
return result;
}
In this example I am using a simple example web script that echoes back your JSON and switches the result based on the value of the "echo" argument. So when it is called with "false", the JSON returns false and the evaluator returns false.
I should probably point out the name collision between the org.json.simple.JSONObject that the evaluate method expects and the org.json.JSONObject I am using to snag the result from the response JSON.
I am attempting to write some unit tests for a class I am writing in Flex 4.5.1 using FlexUnit 4 and Mockolate for my testing and mocking framework respectively. I am using as3-signals for my custom events.
The functionality that I am writing and testing is a wrapper class (QueryQueue) around the QueryTask class within the ArcGIS API for Flex. This enables me to easily queue up multiple query tasks for execution. My wrapper, QueryQueue will dispatch a completed event when all the query responses have been processed.
The interface is very simple.
public interface IQueryQueue
{
function get inProgress():Boolean;
function get count():int;
function get completed():ISignal;
function get canceled():ISignal;
function add(query:Query, url:String, token:Object = null):void;
function cancel():void;
function execute():void;
}
Here is an example usage:
public function exampleUsage():void
{
var queryQueue:IQueryQueue = new QueryQueue(new QueryTaskFactory());
queryQueue.completed.add(onCompleted);
queryQueue.canceled.add(onCanceled);
var query1:Query = new Query();
var query2:Query = new Query();
// set query parameters
queryQueue.add(query1, url1);
queryQueue.add(query2, url2);
queryQueue.execute();
}
public function onCompleted(sender:Object, event:QueryQueueCompletedEventArgs)
{
// do stuff with the the processed results
}
public function onCanceled(sender:Object, event:QueryQueueCanceledEventArgs)
{
// handle the canceled event
}
For my tests I am currently mocking the QueryTaskFactory and QueryTask objects. Simple tests such as ensuring that queries are added to the queue relatively straight forward.
[Test(Description="Tests adding valid QueryTasks to the QueryQueue.")]
public function addsQuerys():void
{
var queryTaskFactory:QueryTaskFactory = nice(QueryTaskFactory);
var queryQueue:IQueryQueue = new QueryQueue(queryTaskFactory);
assertThat(queryQueue.inProgress, isFalse());
assertThat(queryQueue.count, equalTo(0));
var query1:Query = new Query();
queryQueue.add(query1, "http://gisinc.com");
assertThat(queryQueue.inProgress, isFalse());
assertThat(queryQueue.count, equalTo(1));
var query2:Query = new Query();
queryQueue.add(query2, "http://gisinc.com");
assertThat(queryQueue.inProgress, isFalse());
assertThat(queryQueue.count, equalTo(2));
var query3:Query = new Query();
queryQueue.add(query3, "http://gisinc.com");
assertThat(queryQueue.inProgress, isFalse());
assertThat(queryQueue.count, equalTo(3));
}
However, I want to be able to test the execute method as well. This method should execute all the queries added to the queue. When all the query results have been processed the completed event is dispatched. The test should ensure that:
execute is called on each query once and only once
inProgress = true while the results have not been processed
inProgress = false when the results have been processed
completed is dispatched when the results have been processed
canceled is never called (for valid queries)
The processing done within the queue correctly processes and packages the query results
So far I can write tests for items 1 through 5 thanks in large part to the answer provided by weltraumpirat. My execute test now currently looks like this.
[Test(async, description="Tests that all queryies in the queue are executed and the completed signal is fired")]
public function executesAllQueriesInQueue():void
{
// Setup test objects and mocks
var query:Query = new Query();
var mockedQueryTask:QueryTask = nice(QueryTask);
var mockedQueryTaskFactory:QueryTaskFactory = nice(QueryTaskFactory);
// Setup expectations
expect(mockedQueryTaskFactory.createQueryTask("http://test.com")).returns(mockedQueryTask);
expect(mockedQueryTask.execute(query, null)).once();
// Setup handlers for expected and not expected signals (events)
var queryQueue:IQueryQueue = new QueryQueue(mockedQueryTaskFactory);
handleSignal(this, queryQueue.completed, verifyOnCompleted, 500, null);
registerFailureSignal(this, queryQueue.canceled);
// Do it
queryQueue.add(query, "http://test.com");
queryQueue.execute();
// Test that things went according to plan
assertThat(queryQueue.inProgress, isTrue());
verify(mockedQueryTask);
verify(mockedQueryTaskFactory);
function verifyOnCompleted(event:SignalAsyncEvent, passThroughData:Object):void
{
assertThat(queryQueue.inProgress, isFalse());
}
}
The QueryQueue.execute method looks like this.
public function execute():void
{
_inProgress = true;
for each(var queryObject:QueryObject in _queryTasks)
{
var queryTask:QueryTask = _queryTaskFactory.createQueryTask(queryObject.url);
var asyncToken:AsyncToken = queryTask.execute(queryObject.query);
var asyncResponder:AsyncResponder = new AsyncResponder(queryTaskResultHandler, queryTaskFaultHandler, queryObject.token);
asyncToken.addResponder(asyncResponder);
}
}
private function queryTaskResultHandler(result:Object, token:Object = null):void
{
// For each result collect the data and stuff it into a result collection
// to be sent via the completed signal when all querytask responses
// have been processed.
}
private function queryTaskFaultHandler(error:FaultEvent, token:Object = null):void
{
// For each error collect the error and stuff it into an error collection
// to be sent via the completed signal when all querytask responses
// have been processed.
}
For test #6 above what I want to be able to do is to test that the data that is returned in the queryTaskResultHandler and the queryTaskFaultHandler is properly processed.
That is, I do not dispatch a completed event until all the query responses have returned, including successful and failed result.
To test this process I think that I need to mock the data coming back in the result and fault handlers for each mocked query task.
So, how do I mock the data passed to a result handler created via an AsyncResponder using FlexUnit and mockolate.
You can mock any object or interface with mockolate. In my experience, it is best to set up a rule and mock like this:
[Rule]
public var rule : MockolateRule = new MockolateRule();
[Mock]
public var task : QueryTask;
Notice that you must instantiate the rule, but not the mock object.
You can then specify your expectations:
[Test]
public function myTest () : void {
mock( task ).method( "execute" ); // expects that the execute method be called
}
You can expect a bunch of things, such as parameters:
var responder:AsyncResponder = new AsyncResponder(resultHandler, faultHandler);
mock( task ).method( "execute" ).args( responder ); // expects a specific argument
Or make the object return specific values:
mock( queue ).method( "execute" ).returns( myReturnValue ); // actually returns the value(!)
Sending events from the mock object is as simple as calling dispatchEvent on it - since you're mocking the original class, it inherits all of its features, including EventDispatcher.
Now for your special case, it would to my mind be best to mock the use of all three external dependencies: Query, QueryTask and AsyncResponder, since it is not their functionality you are testing, but that of your Queue.
Since you are creating these objects within your queue, that makes it hard to mock them. In fact, you shouldn't really create anything directly in any class, unless there are no external dependencies! Instead, pass in a factory (you might want to use a dependency injection framework) for each of the objects you must create - you can then mock that factory in your test case, and have it return mock objects as needed:
public class QueryFactory {
public function createQuery (...args:*) : Query {
var query:Query = new Query();
(...) // use args array to configure query
return query;
}
}
public class AsyncResponderFactory {
public function createResponder( resultHandler:Function, faultHandler:Function ) : AsyncResponder {
return new AsyncResponder(resultHandler, faultHandler);
}
}
public class QueryTaskFactory {
public function createTask (url:String) : QueryTask {
return new QueryTask(url);
}
}
... in Queue:
(...)
public var queryFactory:QueryFactory;
public var responderFactory : AsyncResponderFactory;
public var taskFactory:QueryTaskFactory;
(...)
var query:Query = queryFactory.createQuery ( myArgs );
var responder:AsyncResponder = responderFactory.createResponder (resultHandler, faultHandler);
var task:QueryTask = taskFactory.createTask (url);
task.execute (query, responder);
(...)
...in your Test:
[Rule]
public var rule : MockolateRule = new MockolateRule();
[Mock]
public var queryFactory:QueryFactory;
public var query:Query; // no need to mock this - you are not calling any of its methods in Queue.
[Mock]
public var responderFactory:AsyncResponderFactory;
public var responder:AsyncResponder;
[Mock]
public var taskFactory:QueryTaskFactory;
[Mock]
public var task:QueryTask;
[Test]
public function myTest () : void {
query = new Query();
mock( queryFactory ).method( "createQuery ").args ( (...) ).returns( query ); // specify arguments instead of (...)!
responder = new AsyncResponder ();
mock( responderFactory ).method( "createResponder" ).args( isA(Function) , isA(Function) ).returns( responder ); // this will ensure that the handlers are really functions
queue.responderFactory = responderFactory;
mock( task ).method( "execute" ).args( query, responder );
mock( taskFactory ).method( "createTask" ).args( "http://myurl.com/" ).returns( task );
queue.taskFactory = taskFactory;
queue.doStuff(); // execute whatever the queue should actually do
}
Note that you must declare all mocks as public, and all of the expectations must be added before passing the mock object to its host , otherwise, mockolate cannot configure the proxy objects correctly.