Moq Callback not working with 3 parameter method - c#

I'm trying to understand why the following unit test does not execute the callback. If I modify the code so that the UpdateWorklowInstanceState method only contains 2 parameters (Guid and IList), it works. However, something about having 3 parameters interferes.
What I mean by interferes is that the Callback doesn't appear to get executed. There's no error message. I expect to see the "Error Occurred" message but instead receive an "Element Updated" message which means the Callback did not populate the resultMessages with the NotificationMessage.
public void BusinessObjectReturnsErrorNotification_ReturnErrorMessage()
{
var workflowInstanceGuid = Guid.NewGuid();
var workflowElementModel = new WorkflowElementModel
{
ElementName = "ValidName",
WorkflowInstanceId = workflowInstanceGuid.ToString()
};
var workflowElementInstance = new WorkflowElementInstance
{
ElementName = workflowElementModel.ElementName,
FullDescription = "Full Description",
SummaryDescription = "Summary Description",
RefTime = DateTime.Now,
ElementType = "ElementType"
};
var mockWebApiBusinessObject = new Mock<IWebApiBusinessObject>();
mockWebApiBusinessObject.Setup(m => m.UpdateWorkflowInstanceState(workflowInstanceGuid, workflowElementInstance, It.IsAny<List<NotificationMessage>>()))
.Callback<Guid, WorkflowElementInstance, IList<NotificationMessage>>(
(workflowInstanceId, elementDetails, resultMessages) =>
{
resultMessages.Add(new NotificationMessage("An Error Occured!", MessageSeverity.Error));
});
var controller = new WorkflowElementController(mockWebApiBusinessObject.Object);
var result = controller.UpdateWorkflowElement(workflowElementModel);
Assert.AreEqual("An Error Occured!", result.Content.ReadAsStringAsync().Result);
}
Method under test:
[HttpPost]
[ActionName("UpdateWorkflowElement")]
public HttpResponseMessage UpdateWorkflowElement(WorkflowElementModel workflowElementModel)
{
if (!ModelState.IsValid || workflowElementModel == null)
{
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
var response = new HttpResponseMessage(HttpStatusCode.OK);
string responseMessage;
if (workflowElementModel.RefTime == DateTime.MinValue)
{
workflowElementModel.RefTime = DateTime.UtcNow;
}
var resultMessages = new List<NotificationMessage>();
var instanceId = new Guid();
if (string.IsNullOrWhiteSpace(workflowElementModel.WorkflowInstanceId) ||
string.IsNullOrWhiteSpace(workflowElementModel.ElementName))
{
responseMessage = "WorkflowInstanceId or ElementName are null or empty";
}
else if (!Guid.TryParse(workflowElementModel.WorkflowInstanceId, out instanceId))
{
responseMessage = "WorkflowInstanceId is not a valid Guid";
}
else
{
var element = new WorkflowElementInstance
{
ElementName = workflowElementModel.ElementName,
RefTime = workflowElementModel.RefTime,
SummaryDescription = workflowElementModel.SummaryDescription ?? "",
FullDescription = workflowElementModel.FullDescription ?? ""
};
_webApiBusinessObject.UpdateWorkflowInstanceState(instanceId, element, resultMessages);
responseMessage = "Element Updated";
}
if (NotificationMessage.HasErrors(resultMessages))
{
responseMessage = resultMessages.Find(m => m.Status == MessageSeverity.Error).Message;
}
response.Content = new StringContent(responseMessage);
return response;
}

It does not work in you case for 3 parameters because you are mixing the expression parameter types.
It.IsAny<List<NotificationMessage>>()
in the setup, as apposed to the
IList<NotificationMessage>
in the callback parameters.
That means the setup expression parameters does not match the callback expression parameters so the call back is not going to be called.
Stick with one type so either go with the List<NotificationMessage> for both
You are also creating new instances of the parameters in the method under test, which would be different instance to the ones used in the setup. That is why the call back is not working. To prove it. Use It.IsAny<>() for all the parameters and it should work
mockWebApiBusinessObject
.Setup(m => m.UpdateWorkflowInstanceState(It.IsAny<Guid>(), It.IsAny<WorkflowElementInstance>(), It.IsAny<List<NotificationMessage>>()))
.Callback<Guid, WorkflowElementInstance, List<NotificationMessage>>(
(workflowInstanceId, elementDetails, resultMessages) =>
{
resultMessages.Add(new NotificationMessage("An Error Occured!", MessageSeverity.Error));
});
Or the more generic interface
mockWebApiBusinessObject
.Setup(m => m.UpdateWorkflowInstanceState(It.IsAny<Guid>(), It.IsAny<WorkflowElementInstance>(), It.IsAny<IList<NotificationMessage>>()))
.Callback<Guid, WorkflowElementInstance, IList<NotificationMessage>>(
(workflowInstanceId, elementDetails, resultMessages) =>
{
resultMessages.Add(new NotificationMessage("An Error Occured!", MessageSeverity.Error));
});
You should also take some time and review Moq Quickstart to get a better understanding of how to use the mocking framework.

Please consider updating at minor places in your unit test.
Add before mocking IWebApiBusinessObject object:
List<NotificationMessage> messages = new List<NotificationMessage>();
Additionally, update Callback :
var mock = new Mock<IWebApiBusinessObject>();
mock.
Setup(m => m.UpdateWorkflowInstanceState(It.IsNotNull<Guid>(), It.IsNotNull<WorkflowElementInstance>(),It.IsAny<List<NotificationMessage>>() )).
Callback(() =>
{
messages.Add(new NotificationMessage("error msg", MessageSeverity.Severe));
messages.Add(new NotificationMessage("Ignore Message", MessageSeverity.Normal)); // this is optional... u can remove it if u want.
});
And need to update the source code method UpdateWorkflowElement(WorkflowElementModel model) to
UpdateWorkflowElement(WorkflowElementModel model, List<NotificationMessage> messages);
Consider changes in unit test code calling UpdateWorkflowElement to
var result = controller.UpdateWorkflowElement(workflowElementModel, messages);
If I have understood your UpdateWorkflowInstanceState() method correctly,
then you are using IWebApiBusinessObject to call UpdateWorkflowInstanceState( , , ) method.
When UpdateWorkflowInstanceState( , , ) executes during unit testing, it fires the Callback in your unit test and adds messages in list of NotificationMessage.

Related

Moq Dapper - Multi-map error: splitOn column 'Id' was not found

I'm running into this problem, let me describe it with my code: https://github.com/UnoSD/Moq.Dapper/issues/20
I'm trying to mock a QueryAsync call using Moq.Dapper library and hitting the error:
Multi-map error: splitOn column 'Id' was not found
Here is the multi mapping query and code that works perfect:
var query = $#" SELECT * FROM [{nameof(Dashboard)}] dashboard
left join [{nameof(DashboardUser)}] dusers on dashboard.DashboardId = dusers.DashboardId
left join [Users] users on dusers.UserId = users.Id
WHERE dashboard.{nameof(accountId)} = #{nameof(accountId)} AND dashboard.{nameof(dashboardId)} = #{nameof(dashboardId)}";
Dashboard? dashboardEntity = null;
using (var connection = _context.CreateConnection())
{
var result = await connection.QueryAsync<Dashboard, DashboardUser, User, Dashboard?>(query,
(dashboard, dashboardUser, user) =>
{
if (dashboard == null) return null;
if (dashboardEntity == null)
{
dashboardEntity = dashboard;
dashboardEntity.Users = new List<DashboardUser>();
}
if (dashboardUser != null)
{
dashboardEntity.Users.Add(dashboardUser);
if (user != null) dashboardUser.User = user;
}
return dashboardEntity;
}, splitOn: $#"{nameof(Dashboard.DashboardId)},{nameof(Dashboard.DashboardId)},Id", param: new { accountId, dashboardId });
}
When Mocking the QueryAsync call I'm hitting the same problem as everyone else in the GitHub thread:
Func<Dashboard, DashboardUser, User, Dashboard?> funMap = (Dashboard, DashboardUser, User) => Mock.Of<Dashboard>();
public async Task Should_Get_Dashboard()
{
// Arrange
var connection = new Mock<DbConnection>();
IEnumerable<Dashboard> expected = GenFu.ListOf<Dashboard>(1);
connection.SetupDapperAsync(c => c.QueryAsync(It.IsAny<string>(), funMap, It.IsAny<object>(), It.IsAny<IDbTransaction>(),It.IsAny<bool>(),
"DashboardId,DashboardId,Id", // <-- SplitOn defined but error is *Multi-map error: splitOn column 'Id' was not found*
It.IsAny<int?>(), It.IsAny<CommandType?>()))
.ReturnsAsync(expected);
_context.Setup(x => x.CreateConnection()).Returns(connection.Object);
var dashboardRepository = new DashboardRepository(_context.Object, null, null);
CommonResult<Dashboard?> actual = new();
// Act
actual = await dashboardRepository.GetDashboard(3, 1);
}
Does anyone have a solution for this? It's the final thing for my Unit Test code coverage.
UPDATE:
I don't need to specify the mapping. See how the code TFirst, TSecond, TThird, TResult is faint/dimmed and I get the same problem even with it explicitly mapped:
I did try this method:
connection.SetupDapperAsync(c => c.QueryAsync<Dashboard>(It.IsAny<string>(), It.IsAny<Type[]>(), GetFunc, It.IsAny<object>(), It.IsAny<IDbTransaction>(), It.IsAny<bool>(), $#"{nameof(Dashboard.DashboardId)},{nameof(Dashboard.DashboardId)},Id", It.IsAny<int?>(), It.IsAny<CommandType?>()))
.ReturnsAsync(expected);
and got the error:
System.ArgumentException : When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id (Parameter 'splitOn')
I checked the Dapper code and tried * for the SplitOn and that made no difference.
UPDATE 2:
The problem is the MultiMapAsync method and I can't mock it because its scope is private:
UPDATE 3:
I changed the Dapper method to Public to see if I could Mock it:
Func<Dashboard, DashboardUser, User, Dashboard?> funMap = (Dashboard, DashboardUser, User) => Mock.Of<Dashboard>();
private class DontMap { /* hiding constructor */ }
public async Task Should_Get_Dashboard()
{
// Arrange
var connection = new Mock<DbConnection>();
IEnumerable<Dashboard> expected = GenFu.ListOf<Dashboard>(1);
connection.SetupDapperAsync(c => c.MultiMapAsync<Dashboard, DashboardUser, User, DontMap, DontMap, DontMap, DontMap, Dashboard?>(new CommandDefinition(It.IsAny<string>(), It.IsAny<object>(), It.IsAny<IDbTransaction>(), It.IsAny<int>(),It.IsAny<CommandType>(), CommandFlags.None, default), funMap, $#"{nameof(Dashboard.DashboardId)},{nameof(Dashboard.DashboardId)},Id")).ReturnsAsync(expected);
I get the error:
Specified method is not supported
Mocking Dapper extension methods provides no value. You end up just testing your own test code. I would do either an integration test, where you test the whole method on a small database, or pull the testable logic out of the Dapper call like this:
using (var connection = _context.CreateConnection())
{
var result = await connection.QueryAsync<Dashboard, DashboardUser, User, Dashboard?>(query,
(dashboard, dashboardUser, user) => CreateDashboardEntity(dashboardEntity, dashboard, dashboardUser, user),
splitOn: $#"{nameof(Dashboard.DashboardId)},{nameof(Dashboard.DashboardId)},Id", param: new { accountId, dashboardId });
}
public Dashboard? CreateDashboardEntity(Dashboard? dashboardEntity, Dashboard dashboard, DashboardUser dashboardUser, User user)
{
if (dashboard == null) return null;
if (dashboardEntity == null)
{
dashboardEntity = dashboard;
dashboardEntity.Users = new List<DashboardUser>();
}
if (dashboardUser != null)
{
dashboardEntity.Users.Add(dashboardUser);
if (user != null) dashboardUser.User = user;
}
return dashboardEntity;
}
Now you can unit-test CreateDashBoardEntity as much as you like, and there is no logic left in QueryAsync that haven't already been tested by the good Dapper developers.

Unit test result in null object while testing a web api 2

I am new to MS Unit Testing and Moq objects. I am trying to test my Web API 2 controller. I have given below my unit test and controller code. While stepping through the code, it doesn't even go to the GetDeliveryCodeStrategy method.
[TestMethod]
public void CreateDelivery_ShouldReturnDeliveryCode()
{
Mock<IDeliveryStrategy> deliveryStrategy = new Mock<IDeliveryStrategy>
();
Mock<IDeliveryCode> deliveryCode = new Mock<IDeliveryCode>();
var controller = new DeliveryCodeController(deliveryStrategy.Object,
deliveryCode.Object);
var controllerContext = new HttpControllerContext();
var request = new HttpRequestMessage();
request.Headers.Add("appToken", "a57ffa87-950e-40f4-b965-17788becac7d");
controllerContext.Request = request;
controller.ControllerContext = controllerContext;
var result = controller.CreateDelivery(50) as
CreatedNegotiatedContentResult<IDeliveryCode>;
Assert.IsNotNull(result);
}
public class DeliveryCodeController : ApiController
{
IDeliveryStrategy _deliveryBatch;
IDeliveryCode _deliveryCode;
//Constructor dependency injection through Autofac
public DeliveryCodeController(IDeliveryStrategy DeliveryBatch,
IDeliveryCode deliveryCode)
{
_deliveryBatch = DeliveryBatch;
_deliveryCode = deliveryCode;
}
[HttpPost]
[Route("api/DeliveryCode/{percentage}")]
public IHttpActionResult CreateDelivery(int percentage)
{
String appToken = String.Empty;
if (Request.Headers.TryGetValues("appToken", out IEnumerable<String>
headerValues))
{
appToken = headerValues.FirstOrDefault();
}
if (!String.IsNullOrEmpty(appToken)))
{
IDeliveryContext deliveryContext =
_deliveryBatch.GetDeliveryCodeStrategy(percentage);
_deliveryCode.Code = deliveryContext.Create();
return Created(Request.RequestUri.ToString(), _deliveryCode);
}
else
{
return Content(HttpStatusCode.Forbidden, new Error { message = "The App
Token is not valid." });
}
}
}
When I do the "Debug Test" and step through the code, the deliveryContext
object comes as null in the code IDeliveryContext deliveryContext =
_deliveryBatch.GetDeliveryCodeStrategy(percentage);
You have to set up the Mock to return a certain value:
IDeliveryContext deliveryContext = // ???? - whatever you want it to be.
// Could be another Mock.
// This is what the Mock will return.
Mock<IDeliveryStrategy> deliveryStrategy = new Mock<IDeliveryStrategy>();
deliveryStrategy.Setup(x => x.GetDeliveryCodeStrategy(It.IsAny<decimal>()))
.Returns(deliveryContext);
This tells the Mock that that when its GetDeliveryCodeStrategy method is called, it should return the specified IDeliveryContext. Depending on what you're trying to do, that could be another Mock. (Mocks that return mocks are undesirable, but if you're starting out I'd file that detail away and come back to it.)
I'm guessing that percentage is a decimal. It.IsAny<decimal>() means that the mock doesn't care what the value is. That's usually okay because what you're testing is what your class does with the object returned by the mock.
You need to call Setup() on mock objects for the methods that you want to use:
var deliveryStrategy = new Mock<IDeliveryStrategy>();
deliveryStrategy.Setup(x => x.GetDeliveryCodeStrategy(It.IsAny<int>))
.Returns(AMockOfDeliveryContext); //you need to mock it beforehand so you can
//use the object here

Unit testing Web API Controller: Error fetching result

I'm new to unit testing in ASP.NET so please forgive my ignorance on this. I'm trying to test my controller.
This is the function in my controller which I'm testing:
public IHttpActionResult GetCustId(string name)
{
var c_id = db.Customer.Where(s => (s.c_Name == name));
if (c_id == null)
{
return null;
}
return Ok(c_id);
}
And this is my unit test code:
public void GetName_ShouldReturnCorrectId()
{
var context = new TestSContext();
context.Customers.Add(new Customer { c_ID = 1, c_Name = "jonny"});
var controller = new CustomerController(context);
var result = controller.GetCustId("Johnny") as OkNegotiatedContentResult<Customer>; //ISSUE: Result is always NULL
Assert.IsNotNull(result);
Assert.AreEqual(1, result.Content.c_ID);
}
The issue is here:
var result = controller.GetServiceId("Johnny") as OkNegotiatedContentResult<Customer>
because it is always returning NULL.
BUT... If I use just this:
var result = controller.GetCustId("Johnny");
Then the result is not null. And the first assert passes.
But I can't use it because I'm not sure how to check the second assert statement without using result.Content. I'm really not sure what are the best practices to be testing in my case.
Appreciate any help.
You are trying to find "Johnny" (with 'h') when you have put "jonny" into your mock context thus method always returns null due to your if statement
if (c_id == null)
{
return null;
}
Adding to #nizzik's answer, which is correct based on your example, to avoid simple mistakes like that you should store your values in variables and reuse them to make sure that they are as intended.
public void GetName_ShouldReturnCorrectId() {
//Arrange
var name = "Johnny";
var expectedId = 1;
var context = new TestSContext();
context.Customers.Add(new Customer { c_ID = expectedId, c_Name = name});
var controller = new CustomerController(context);
//Act
var result = controller.GetCustId(name) as OkNegotiatedContentResult<Customer>;
//Assert
Assert.IsNotNull(result);
Assert.AreEqual(expectedId, result.Content.c_ID);
}
That way you can change any one of them and the test should execute as expected.

Testing ReactiveCommand async result

I use Xamarin and ReactiveUI to do mobile apps.
I'd like to test my ViewModel and its ReactiveCommand.
I register an asynchronous operation which return an IObservable inside the ReactiveCommand.
In ViewModel constructor :
Login = new ReactiveCommand();
var loginResult = Login.RegisterAsync(_ => Client.Login(Username, Password));
loginResult.ObserveOn(RxApp.MainThreadScheduler).BindTo(this, self => self.ConnectedUser);
Login
.Where(_ => ConnectedUser != null)
.Subscribe(_ => {
ConnectedUser.Disconnect();
ConnectedUser = null;
});
What's in Client.Login :
return Observable.Start(async () => {
// Do an HTTP POST to login the user, with await stuff in
// Return the User on success or throw an exception on error
}, RxApp.TaskpoolScheduler)
.Select(task => task.Result);
How to test the ViewModel login success ? Here is my approach which doesn't work :
[Test]
public void TestLoginSuccess()
{
ViewModel viewModel = new ViewModel();
viewModel.Username = "toto";
viewModel.Password = "****";
viewModel.Login.Execute(null);
Assert.That(viewModel.ConnectedUser, Is.Not.Null); // always null so fail :(
}
The test fails because the assertion is made before the command has finished executing.
Resolution
I was subscribing to the Login command and assumed that my "disconnection block" would be called before the RegisterAsync's one. Inverted the two would resolve the issue or simply put the "disconnection" logic inside RegisterAsync like this :
var loginResult = Login.RegisterAsync(_ => {
if (ConnectedUser != null)
{
ConnectedUser.Disconnect();
ConnectedUser = null;
}
Client.Login(Username, Password);
});
Paul Betts ' solution was also necessary.
TaskPoolScheduler is not set to Scheduler.Immediate, so it really is running in the background. Try this:
Scheduler.CurrentThread.With(sched => {
// TODO: Write your code here, now both schedulers are
// rigged to CurrentThread.
});

Issue with mocking IOrganizationService.Execute in CRM 2011 plugin

I am still new to mocking and I am having trouble with this code:
//create the request
SendEmailFromTemplateRequest emailUsingTemplateReq =
new SendEmailFromTemplateRequest
{
Target = email,
TemplateId = new Guid("07B94C1D-C85F-492F-B120-F0A743C540E6"),
RegardingId = toParty[0].PartyId.Id,
RegardingType = toParty[0].PartyId.LogicalName
};
//retrieve response
SendEmailFromTemplateResponse emailUsingTemplateResponse =
(SendEmailFromTemplateResponse)service.Execute(emailUsingTemplateReq);
var emailId = emailUsingTemplateResponse.Id;
I have had no problems up to this point mocking the IOrganizationService, but I am doing something wrong with the execute method. According to the sdk the Execute method returns an OrganizationResponse object that needs to be cast into the correct response class. Here is what I have tried so far:
var idResults = new ParameterCollection();
idResults.Add("Id", Guid.NewGuid());
mockOrganizationService
.Setup(os => os.Execute(It.IsAny<SendEmailFromTemplateRequest>()))
.Returns(new OrganizationResponse
{
Results = idResults,
ResponseName = "SendEmailFromTemplate",
});
When I try to run the test I keep getting an invalid cast exception. I figure I must be setting up the response object wrong. Can someone explain to me the correct way to mock the IOrganizationService.Execute method?
Your approach is correct, but you use the wrong response type. The service returns the results as OrganizationResponse (which is the base class for all responses). You try to cast the base type into a specific type. This doesn't work.
You simply have to return an instance of SendEmailFromTemplateResponse to get your code working.
var orgService = new Mock<IOrganizationService>();
var idResults = new ParameterCollection
{
{"Id", Guid.NewGuid()}
};
orgService.Setup(s => s.Execute(It.IsAny<SendEmailFromTemplateRequest>()))
.Returns(new SendEmailFromTemplateResponse
{
Results = idResults,
ResponseName = "SendEmailFromTemplate"
});

Categories