I'd like to Moq the response from the ElasticClient to show an error or some information in the DebugInformation property.
I have the following:
ISearchResponse<person> personResponse = new SearchResponse<person>();
personResponse.ApiCall = new ApiCallDetails
{
Success = false,
};
personResponse.DebugInformation = "Something happened"; //This is not allowed
_elasticClient.Setup(s =>
s.Search<person>(
It.IsAny<Func<SearchDescriptor<person>, ISearchRequest>>()))
.Returns(personResponse);
I am unable to assign to DebugInformation as it is readonly. Any ideas?
Someone suggested I Moq the Interface of the response instead
So I mocked it Like this
Mock<ISearchResponse<person>> mockResponse = new Mock<ISearchResponse<person>>();
mockResponse.Setup(s => s.ApiCall.DebugInformation).Returns("Something");
mockResponse.Setup(s => s.ApiCall.Success).Returns(false);
mockResponse.Setup(s => s.Hits).Returns(new List<IHit<person>>());
_elasticClient.Setup(s =>
s.Search<person>(
It.IsAny<Func<SearchDescriptor<person>, ISearchRequest>>()))
.Returns(mockResponse.Object);
... and this works!
Related
I have a method
Task<Document> GetCampaignById(Tenant tenant, string language, string campaignId);
that I am trying to mock:
mockLocator.Setup(l => l.GetCampaignById(
It.IsAny<Tenant>(),
It.IsAny<string>(),
It.IsAny<string>()))
.Returns(Task.FromResult(dummy));
I did, at first have "not set up" output but I am not getting any at all now and the test just shows as failed. I have tried changing to ReturnsAsync(dummy) but that did not alter my output at all. moq is set to strict but I don't think that should matter.
As far as I can tell I am setting this up with the correct parameter types and the correct return type but the test seems to fail before it even starts. Can anyone shed any light on this?
Full Test
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
[TestCase(4)]
[TestCase(5)]
public async Task ShouldCallUpdaterForEveryTenantResolved(int tupleCount)
{
// Arrange
var tenantTuples = CreateTenantTuples(tupleCount);
var mockTenantResolver = MockRepository.Create<ITenantResolver>();
mockTenantResolver.Setup(tr => tr.GetTenantTuples(It.IsAny<string>()))
.Returns(tenantTuples);
var mockCampaignIngestionService = MockRepository.Create<ICampaignIngestionService>();
var dummy = new Document();
var mockLocator = MockRepository.Create<IVehicleSearchStockUpdater>();
mockLocator.Setup(l => l.GetCampaignById(
It.IsAny<Tenant>(),
It.IsAny<string>(),
It.IsAny<string>()))
.Returns(Task.FromResult(dummy));
mockLocator.Setup(l => l.UpsertNewStockLevel(
It.IsAny<Document>(),
It.IsAny<OmegaConnector.Functions.PartnerCommunicator.Models.ChangeStockInfo>()))
.Returns(Task.FromResult(dummy));
var sut = new CampaignIngestionProcessor(
mockTenantResolver.Object,
mockCampaignIngestionService.Object,
mockLocator.Object);
var data = GetSimpleTestData();
data.NewStock = 5;
// Act
await sut.SubmitChangeStock(data);
// Assert
mockLocator.Verify(l => l.GetCampaignById(
It.IsAny<Tenant>(),
It.IsAny<string>(),
It.IsAny<string>()),
Times.Exactly(tupleCount)
);
mockLocator.Verify(l => l.UpsertNewStockLevel(
It.IsAny<Document>(),
It.IsAny<OmegaConnector.Functions.PartnerCommunicator.Models.ChangeStockInfo>()),
Times.Exactly(tupleCount)
);
}
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.
I have a ICreateService class that has dependency on ITicketApiAdapter. I've tried registering a mock ITicketAdaper so that it gets injected when I create an anonymous create service.
So, in setup, I have this register for the ticket adapter:
Fixture.Register(() =>
{
var ticketApiAdapter = new Mock<ITicketApiAdapter>();
ticketApiAdapter
.Setup( x => x.AddTicketComment(
It.IsAny<User>(),
It.IsAny<Customer>(),
It.IsAny<TicketComment>()))
.Returns(new SaveResult
{
Success = true,
Id = Fixture.CreateAnonymous<Guid>().ToString()
});
return ticketApiAdapter;
});
Fixture.Register(() => new CreateService(Fixture.CreateAnonymous<Mock<ITicketApiAdapter>>().Object));
From my understanding, that should "freeze" both the ICreateService and Mock<ITicketApiAdapter> so that when I request an anonymous instance, it's the one I registered.
I have a test that looks like this:
[TestMethod]
public void CreateServiceCallsAddTicketComment()
{
var apiTicketAdapter = Fixture.CreateAnonymous<Mock<ITicketApiAdapter>>();
var createTicketRequest = Fixture.CreateAnonymous<CreateTicketComment>();
var createService = Fixture.CreateAnonymous<CreateService>();
var results = createService.CreateTicketComment(createTicketRequest);
apiTicketAdapter
.Verify(x => x.AddTicketComment(
It.IsAny<User>(),
It.IsAny<Customer>(),
It.IsAny<TicketComment>()),
Times.Once());
Assert.IsTrue(results.All(x => x.Success));
Assert.IsTrue(results.All(x => x.Errors.Count == 0));
}
I expect the apiTicketAdapter to be the one I registered so that I can verify the method is called. If I step through, the TicketApiAdapter is called, but Moq says it wasn't.
Edit
This is the error I get:
CreateServiceCallsAddTicketComment threw exception:
Moq.MockException: Expected invocation on the mock once, but was 0
times: x => x.AddTicketComment(It.IsAny(), It.IsAny(),
It.IsAny())
Configured setups: x => x.AddTicketComment(It.IsAny(),
It.IsAny(), It.IsAny()), Times.Never No
invocations performed.
When you Register a code block, that code block is going to be invoked every time the Fixture instance resolves the requested type. This means that it's not frozen. If you want to Freeze something, one of the Freeze overloads are often easier to use.
Better yet, since you seem to be using Moq, may I suggest using the AutoMoq extension?
That would enable you to rewrite the test to something like this:
[TestMethod]
public void CreateServiceCallsAddTicketComment(new AutoMoqCustomization());
{
var fixture = new Fixture().Customize()
var apiTicketAdapter = fixture.Freeze<Mock<ITicketApiAdapter>>();
ticketApiAdapter
.Setup( x => x.AddTicketComment(
It.IsAny<User>(),
It.IsAny<Customer>(),
It.IsAny<TicketComment>()))
.Returns(new SaveResult
{
Success = true,
Id = Fixture.CreateAnonymous<Guid>().ToString()
});
var createTicketRequest = fixture.Freeze<CreateTicketComment>();
var createService = fixture.CreateAnonymous<CreateService>();
var results = createService.CreateTicketComment(createTicketRequest);
apiTicketAdapter
.Verify(x => x.AddTicketComment(
It.IsAny<User>(),
It.IsAny<Customer>(),
It.IsAny<TicketComment>()),
Times.Once());
Assert.IsTrue(results.All(x => x.Success));
Assert.IsTrue(results.All(x => x.Errors.Count == 0));
}
That's assuming that CreateTicketRequest uses Constructor Injection or Property Injection.
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"
});
I am using Moq as my mocking framework. As per the code below, I have two mocks setup and I would like to setup the second to return the first mock. Is this possible and if so how would I go about doing it? At the moment it says the mock being returned is an invalid candidate.
[SetUp]
private void SetupMarketRow()
{
var marketTotalRow = new Mock<ITotalRow>();
marketTotalRow.Setup(r => r.TotalBudgetCurrentFact).Returns(1860716);
marketTotalRow.Setup(r => r.TotalBudgetEvol).Returns(-26);
marketTotalRow.Setup(r => r.TotalBudgetPreviousFact).Returns(2514079);
var localMarketReport = new Mock<IReport>();
localMarketReport.Setup(r => r.TotalRow).Returns(marketTotalRow);
// Red swiggley here saying invalid candidate
}
You can access the actual Mocked ITotalRow using marketTotalRow.Object.
[SetUp]
private void SetupMarketRow()
{
var marketTotalRow = new Mock<ITotalRow>();
marketTotalRow.Setup(r => r.TotalBudgetCurrentFact).Returns(1860716);
marketTotalRow.Setup(r => r.TotalBudgetEvol).Returns(-26);
marketTotalRow.Setup(r => r.TotalBudgetPreviousFact).Returns(2514079);
var localMarketReport = new Mock<IReport>();
localMarketReport.Setup(r => r.TotalRow).Returns(marketTotalRow.Object);
// Red swiggley here saying invalid candidate
}
Changing the interface declaration from
MarketTotalRow TotalRow { get; }
to...
ITotalRow TotalRow { get; }
fixed the problem.