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"
});
Related
I am trying to write a unit test that verifies that adding a new property to an Avro schema is backwards compatible.
First I took the Avro generated .cs model and saved it as MyModelOld.cs and renamed the class inside to MyModelOld.
Then I re-ran Avro gen against the avsc file with the new property.
What I'm trying to do is this:
var schemaRegistry = -> something that doesn't require a running docker image <-;
var deserializerOld = new AvroDeserializer<MyModelOld>(schemaRegistry);
var serializerNew = new AvroSerializer<MyModel>(schemaRegistry);
var myModel = new MyModel() {...};
var myModelBytes = await serializerNew.SerializeAsync(myModel, new());
var myModelOld = await deserializerOld.DeserializeAsync(myModelBytes, false, new());
// Check properties...
Then I was going to go the opposite direction and check that the new property uses the specified default value.
The problem I'm having is what to use for the schema registry. I don't want to have a docker image running for these tests because I don't think it shouldn't be necessary.
I've tried a mock of ISchemaRegistry, but it appears to need a fully functional class in order for serialize/deserialize to work.
I could probably walk through the logic for CachedSchemaRegistryClient and try to munge it to work, but before I do so I'd like to find out if someone knows of an ISchemaRegistry implementaion that would work for my use case.
Has anyone tried to write tests to validate backwards compatibility of Avro schema updates?
If so, how did you go about doing so?
Thanks.
I ended up doing it this way:
private ISchemaRegistryClient NewTestRegistry(string topic)
{
// Code to mock SchemaRegistry taken from:
// https://github.com/confluentinc/confluent-kafka-dotnet/blob/master/test/Confluent.SchemaRegistry.Serdes.UnitTests/SerializeDeserialize.cs
Dictionary<string, int> store = new Dictionary<string, int>();
var schemaRegistryMock = new Mock<ISchemaRegistryClient>();
#pragma warning disable CS0618 // Type or member is obsolete
schemaRegistryMock.Setup(x => x.ConstructValueSubjectName(topic, It.IsAny<string>()))
.Returns($"{topic}-value");
schemaRegistryMock.Setup(x => x.RegisterSchemaAsync($"{topic}-value", It.IsAny<string>(), It.IsAny<bool>()))
.ReturnsAsync((string topic, string schema, bool normalize) =>
store.TryGetValue(schema, out int id) ? id : store[schema] = store.Count + 1
);
#pragma warning restore CS0618 // Type or member is obsolete
schemaRegistryMock.Setup(x => x.GetSchemaAsync(It.IsAny<int>(), It.IsAny<string>()))
.ReturnsAsync((int id, string format) =>
new Schema(store.Where(x => x.Value == id).First().Key, null, SchemaType.Avro)
);
return schemaRegistryMock.Object;
}
[TestMethod]
public async Task BackwardsCompatible()
{
var topic = "MyCoolTopic";
var schemaRegistry = NewTestRegistry(topic);
var context = new SerializationContext(MessageComponentType.Value, topic);
var deserializerOld = new AvroDeserializer<MyModelOld>(schemaRegistry);
var serializerNew = new AvroSerializer<MyModel>(schemaRegistry);
var myModel = new MyModel() { /* Set properties */};
var myModelBytes = await serializerNew.SerializeAsync(myModel, context);
var myModelOld = await deserializerOld.DeserializeAsync(myModelBytes, false, context);
// Check properties...
}
[TestMethod]
public async Task ForwardsCompatible()
{
// Similar to the above test.
}
If you want to test schemas, you don't need Kafka-related serializers; just use raw Avro C# library.
Alternatively, look at the existing tests
var config = new SchemaRegistryConfig { Url = "irrelevanthost:8081" };
var src = new CachedSchemaRegistryClient(config);
Assert...(src... );
I'm using azure blob container to download container content and do some stuff, here is my code:
public async Task<IEnumerable<MyData>> ReadBlobContent(BlobClient blob)
{
var blobContent = await blob.DownloadContentAsync();
var blobContentAsString = Encoding.UTF8.GetString(blobContent.Value.Content.ToArray());
var jsonSerializerOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
return JsonSerializer.Deserialize<IEnumerable<MyData>>(blobContentAsString, jsonSerializerOptions);
}
I want to test the behavior of this code which is already called somewhere else, I'm using .NET Core 3.1 with xUnit and NSubstitute
I'm stuck with substituting the download process, I already did this:
_blobClient = Substitute.For<BlobClient>();
_blobContainerClient = Substitute.For<BlobContainerClient>();
_blobContainerClient
.GetBlobClient(default)
.ReturnsForAnyArgs(_blobClient);
_blobDownloadResult = Substitute.For<Response<BlobDownloadResult>>();
_blobDownloadResult.Value
.Returns(Substitute.For<Func<NSubstitute.Core.CallInfo, BlobDownloadResult>>());
_blobDownloadResult.Value.Content
.Returns(Substitute.For<BinaryData>()); //here it throws System.NullReferenceException: 'Object reference not set to an instance of an object.'
_blobClient.DownloadContentAsync()
.Returns(_blobDownloadResult);
Always the result value returned from the download is null, I want to mock it so that I could retrieve the download content and do the needed assertions.
Could you please challenge me on this issue?
_blobDownloadResult.Value returns null because instead of providing value to Returns method, you provide mock for lambda
_blobDownloadResult.Value
.Returns(Substitute.For<Func<NSubstitute.Core.CallInfo, BlobDownloadResult>>());
What you have to do, is return actual BlobDownloadResult. As its constructor is internal, you need to do it via Azure.Storage.Blobs.Models.BlobsModelFactory. This can look as follows
[Fact]
public async Task Test()
{
_blobClient = Substitute.For<BlobClient>();
_blobContainerClient = Substitute.For<BlobContainerClient>();
_blobContainerClient
.GetBlobClient(default)
.ReturnsForAnyArgs(_blobClient);
_blobDownloadResult = Substitute.For<Response<BlobDownloadResult>>();
var downloadResult = BlobsModelFactory.BlobDownloadResult(BinaryData.FromObjectAsJson(new[]
{
new MyData()
}));
_blobDownloadResult.Value.Returns(downloadResult);
var blobDownloadResult = _blobDownloadResult.Value;
_blobClient.DownloadContentAsync()
.Returns(_blobDownloadResult);
var readBlobContent = await ReadBlobContent(_blobClient);
}
Note, that you can't setup return value of blobDownloadResult.Content with
blobDownloadResult.Content.Returns(Substitute.For<BinaryData>());
it is setup with factory instead
var downloadResult = BlobsModelFactory.BlobDownloadResult(BinaryData.FromObjectAsJson(new[]
{
new MyData()
}));
As you are using NSubstitute, I suggest to install https://www.nuget.org/packages/NSubstitute.Analyzers.CSharp/ which picks up most of mistakes you did in your code
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!
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.
Hi I am trying to use C# reflection to call a method that is passed a parameter and in return passes back a result. How can I do that? I've tried a couple of things but with no success. I'm used to PHP and Python where this can be done on a single line so this is very confusing to me.
In essence this is how the call would be made without reflection:
response = service.CreateAmbience(request);
request has these objects:
request.UserId = (long)Constants.defaultAmbience["UserId"];
request.Ambience.CountryId = (long[])Constants.defaultAmbience["CountryId"];
request.Ambience.Name.DefaultText = (string)Constants.defaultAmbience["NameDefaultText"];
request.Ambience.Name.LanguageText = GetCultureTextLanguageText((string)Constants.defaultAmbience["NameCulture"], (string)Constants.defaultAmbience["NameText"]);
request.Ambience.Description.DefaultText = (string)Constants.defaultAmbience["DescriptionText"];
request.Ambience.Description.LanguageText = GetCultureTextLanguageText((string)Constants.defaultAmbience["DescriptionCulture"], (string)Constants.defaultAmbience["DescriptionDefaultText"]);
This is my function to implement the reflection where serviceAction for the case above would be "CreateAmbience":
public static R ResponseHelper<T,R>(T request, String serviceAction)
{
ICMSCoreContentService service = new ContentServiceRef.CMSCoreContentServiceClient();
R response = default(R);
response = ???
}
Something along the lines of:
MethodInfo method = service.GetType().GetMethod(serviceAction);
object result = method.Invoke(service, new object[] { request });
return (R) result;
You may well want to add checks at each level though, to make sure the method in question is actually valid, that it has the right parameter types, and that it's got the right return type. This should be enough to get you started though.
Here's a quick example of calling an object method by name using reflection:
Type thisType = <your object>.GetType();
MethodInfo theMethod = thisType.GetMethod(<The Method Name>);
theMethod.Invoke(this, <an object [] of parameters or null>);
If you're on .NET 4, use dynamic:
dynamic dService = service;
var response = dService.CreateAmbience(request);
You can use Delegate.CreateDelegate to obtain a delegate to the method by name:
public static R ResponseHelper<T,R>(T request, string serviceAction)
{
var service = new ContentServiceRef.CMSCoreContentServiceClient();
var func = (Func<T,R>)Delegate.CreateDelegate(typeof(Func<T,R>),
service,
serviceAction);
return func(request);
}