How can I mock an AsyncResponder and the resultant handler functions on a mocked object using FlexUnit 4 and mockolate - apache-flex

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.

Related

NullReferenceException in unittesting with NUnit, NSubstitute, and async method

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

Iterate with asynchronous functions

Using the Twitch API and vert.x - I'm looking to continuously send requests to Twitch's API using a WebClient and Twitch's cursor response to go page by page. However I'm not sure how to go back and keep doing queries until a condition is met due to vert.x's asynchronous nature.
Here's my code so far
public void getEntireStreamList(Handler<AsyncResult<JsonObject>> handler) {
JsonObject data = new JsonObject();
getLiveChannels(100, result -> {
if(result.succeeded()) {
JsonObject json = result.result();
String cursor = json.getJsonObject("pagination").getString("cursor");
data.put("data", json.getJsonArray("data"));
if(json.getJsonArray("data").size() < 100) { // IF NOT LAST PAGE
// GO BACK AND DO AGAIN WITH CURSOR IN REQUEST
}
handler.handle(Future.succeededFuture(data));
} else
handler.handle(Future.failedFuture(result.cause()));
});
}
Ideally I'd be able to call getLiveChannels with the cursor String from the previous request to continue the search.
You will need to use Future composition.
Here's my code for your problem:
public void getEntireStreamList(Handler<AsyncResult<JsonObject>> handler) {
JsonArray data = new JsonArray();
// create initial Future for first function call
Future<JsonObject> initFuture = Future.future();
// complete Future when getLiveChannels completes
// fail on exception
getLiveChannels(100, initFuture.completer());
// Create a callback that returns a Future
// for composition.
final AtomicReference<Function<JsonObject, Future<JsonObject>>> callback = new AtomicReference<>();
// Create Function that calls composition with itself.
// This is similar to recursion.
Function<JsonObject, Future<JsonObject>> cb = new Function<JsonObject, Future<JsonObject>>() {
#Override
public Future<JsonObject> apply(JsonObject json) {
// new Future to return
Future<JsonObject> f = Future.future();
// Do what you wanna do with the data
String cursor = json.getJsonObject("pagination").getString("cursor");
data.addAll(json.getJsonArray("data"));
// IF NOT LAST PAGE
if(json.getJsonArray("data").size() == 100) {
// get more live channels with cursor
getLiveChannels(100, cursor, f.completer());
// return composed Future
return f.compose(this);
}
// Otherwise return completed Future with results.
f.complete(new JsonObject().put("data", data));
return f;
}
};
Future<JsonObject> composite = initFuture.compose(cb);
// Set handler on composite Future (ALL composed futures together)
composite.setHandler(result -> handler.handle(result));
}
The code + comments should speak for themselves if you read the Vert.x docs on sequential Future composition.

How to test SignalR 2 hub only notifies a single user?

I've worked through the unit testing examples in the SignalR 2 documentation, but now I'd like to test that my hub only notifies a single user in certain situations.
My hub code looks like this:
public class NotificationsHub : Hub
{
public void RaiseAlert(string message)
{
Clients.All.RaiseAlert(message);
}
public void RaiseNotificationAlert(string userId)
{
if (userId == null)
{
// Notify all clients
Clients.All.RaiseAlert("");
return;
}
// Notify only the client for this userId
Clients.User(userId).RaiseAlert("");
}
}
My unit test for checking that all clients are notified looks like this (it's based on the Microsoft example):
[Test]
public void NotifiesAllUsersWhenNoUserIdSpecified()
{
// Based on: https://learn.microsoft.com/vi-vn/aspnet/signalr/overview/testing-and-debugging/unit-testing-signalr-applications
// Arrange
// This is faking the
var mockClients = new Mock<IClientContract>();
mockClients.Setup(m => m.RaiseAlert(It.IsAny<string>())).Verifiable();
// A mock of our SignalR hub's clients
var mockClientConnCtx = new Mock<IHubCallerConnectionContext<dynamic>>();
mockClientConnCtx.Setup(m => m.All).Returns(mockClients.Object);
// Set the hub's connection context to the mock context
var hub = new NotificationsHub
{
Clients = mockClientConnCtx.Object
};
// Action
hub.RaiseNotificationAlert(null);
// Assert
mockClients.Verify(m => m.RaiseAlert(It.IsAny<string>()));
}
What I'm not sure about is how to change the collection of clients represented by the var mockClients = new Mock<IClientContract>() line into a group of individual clients so that I can then test that if I notify user 1, then users 2 and 3 weren't notified?
I found another question about how to unit test groups and one of the answers pointed to the unit tests for the SignalR codebase.
Looking at those examples I worked out that I needed to add mocking of calls to the User method of the mockClients. That ended up looking like this:
public interface IClientContract
{
void RaiseAlert(string message);
}
[Test]
public void NotifiesOnlySpecifiedUserWhenUserIdSent()
{
// Adapted from code here: https://github.com/SignalR/SignalR/blob/dev/tests/Microsoft.AspNet.SignalR.Tests/Server/Hubs/HubFacts.cs
// Arrange
// Set up the individual mock clients
var client1 = new Mock<IClientContract>();
var client2 = new Mock<IClientContract>();
var client3 = new Mock<IClientContract>();
client1.Setup(m => m.RaiseAlert(It.IsAny<string>())).Verifiable();
client2.Setup(m => m.RaiseAlert(It.IsAny<string>())).Verifiable();
client3.Setup(m => m.RaiseAlert(It.IsAny<string>())).Verifiable();
// set the Connection Context to return the mock clients
var mockClients = new Mock<IHubCallerConnectionContext<dynamic>>();
mockClients.Setup(m => m.User("1")).Returns(client1.Object);
mockClients.Setup(m => m.User("2")).Returns(client2.Object);
mockClients.Setup(m => m.User("3")).Returns(client3.Object);
// Assign our mock context to our hub
var hub = new NotificationsHub
{
Clients = mockClients.Object
};
// Act
hub.RaiseNotificationAlert("2");
// Assert
client1.Verify(m => m.RaiseAlert(It.IsAny<string>()), Times.Never);
client2.Verify(m=>m.RaiseAlert(""), Times.Once);
client3.Verify(m => m.RaiseAlert(It.IsAny<string>()), Times.Never);
}

How to send List of objects to google execution api and handle it in google apps script

In general I want to export data from asp.net mvc application to Google Sheets for example list of people. I've already set up connection and authenticated app with my Google account (trough OAuth2) but now I'm trying to send my list of objects to api and then handle it in script (by putting all data in new file) and couldn't get my head around this.
Here is some sample code in my app that sends the request.
public async Task<ActionResult> SendTestData()
{
var result = new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).
AuthorizeAsync(CancellationToken.None).Result;
if (result.Credential != null)
{
string scriptId = "MY_SCRIPT_ID";
var service = new ScriptService(new BaseClientService.Initializer
{
HttpClientInitializer = result.Credential,
ApplicationName = "Test"
});
IList<object> parameters = new List<object>();
var people= new List<Person>(); // next i'm selecting data from db.Person to this variable
parameters.Add(people);
ExecutionRequest req = new ExecutionRequest();
req.Function = "testFunction";
req.Parameters = parameters;
ScriptsResource.RunRequest runReq = service.Scripts.Run(req, scriptId);
try
{
Operation op = runReq.Execute();
if (op.Error != null)
{
// The API executed, but the script returned an error.
// Extract the first (and only) set of error details
// as a IDictionary. The values of this dictionary are
// the script's 'errorMessage' and 'errorType', and an
// array of stack trace elements. Casting the array as
// a JSON JArray allows the trace elements to be accessed
// directly.
IDictionary<string, object> error = op.Error.Details[0];
if (error["scriptStackTraceElements"] != null)
{
// There may not be a stacktrace if the script didn't
// start executing.
Newtonsoft.Json.Linq.JArray st =
(Newtonsoft.Json.Linq.JArray)error["scriptStackTraceElements"];
}
}
else
{
// The result provided by the API needs to be cast into
// the correct type, based upon what types the Apps
// Script function returns. Here, the function returns
// an Apps Script Object with String keys and values.
// It is most convenient to cast the return value as a JSON
// JObject (folderSet).
Newtonsoft.Json.Linq.JObject folderSet =
(Newtonsoft.Json.Linq.JObject)op.Response["result"];
}
}
catch (Google.GoogleApiException e)
{
// The API encountered a problem before the script
// started executing.
AddAlert(Severity.error, e.Message);
}
return RedirectToAction("Index", "Controller");
}
else
{
return new RedirectResult(result.RedirectUri);
}
}
The next is how to handle this data in scripts - are they serialized to JSON there?
The execution API calls are essentially REST calls so the payload should be serialized as per that. Stringified JSON is typically fine. Your GAS function should then parse that payload to consume the encoded lists
var data = JSON.parse(payload);

How do I set up Moq so that I can unit test adding multiple groups and clients in SignalR?

I've been working on coming with a SignalR Unit testing framework using Moq.
I have been able things to get reasonably well with the 1 group - 1 client (connection) scenario.
How do I set up Moq so I can:
1) Add/remove multiple clients from the same group?
2) Add/remove multiple groups on the same mocked hub?
I'm relatively new to the world of Moq and SignalR combination.
Thanks in advance,
JohnB
Here is an example testing adding a client to multipe groups using Moq and xUnit.net:
[Fact]
public async Task MyHubAddsConnectionToTheCorrectGroups()
{
// Arrange
var groupManagerMock = new Mock<IGroupManager>();
var connectionId = Guid.NewGuid().ToString();
var groupsJoined = new List<string>();
groupManagerMock.Setup(g => g.Add(connectionId, It.IsAny<string>()))
.Returns(Task.FromResult<object>(null))
.Callback<string, string>((cid, groupToJoin) =>
groupsJoined.Add(groupToJoin));
var myHub = new MyHub();
myHub.Groups = groupManagerMock.Object;
myHub.Context = new HubCallerContext(request: null,
connectionId: connectionId);
// Act
await myHub.AddToGroups();
// Assert
groupManagerMock.VerifyAll();
Assert.Equal(3, groupsJoined.Count);
Assert.Contains("group1", groupsJoined);
Assert.Contains("group2", groupsJoined);
Assert.Contains("group3", groupsJoined);
}
public class MyHub : Hub
{
public async Task AddToGroups()
{
await Groups.Add(Context.ConnectionId, "group1");
await Groups.Add(Context.ConnectionId, "group2");
await Groups.Add(Context.ConnectionId, "group3");
}
}
The basic idea is to define a Callback along with your Setup that stores arguments important to your test inside a collection. You can then use the collection verify that the method you mocked was called the right number of times with the right arguments. I don't verify the order of the calls to Groups.Add in my example test, but you can test that as well.
This pattern extends pretty trivially to testing the adding/removing of multiple clients. Basically, you would just need a second collection to store the connectionId arguments passed to Groups.Add.

Resources