I'm using fluentvalidation and lightinject
Here is my code to insert a blog article;
public OperationResultDto Add(BlogArticleDto blogArticleDto)
{
OperationResultDto result = new OperationResultDto();
ValidationResult validationResult =
_blogArticleModelValidator.Validate(blogArticleDto);
if (!validationResult.IsValid)
{
result.IsOk = false;
ValidationFailure firstValidationFailer =
validationResult.Errors.FirstOrDefault();
if (firstValidationFailer != null)
{
result.Message = firstValidationFailer.ErrorMessage;
}
return result;
}
BlogArticle blogArticle = new BlogArticle {
Title = blogArticleDto.Title,
ShortBody = blogArticleDto.ShortBody,
Body = blogArticleDto.Body,
IsOnline = blogArticleDto.IsOnline,
CategoryName = blogArticleDto.CategoryName,
PublishedBy = blogArticleDto.PublishedBy,
PublishDate = blogArticleDto.PublishDate,
Tags = new List<string>(), //TODO parse, model's tags in one string.
CreateDate = DateTime.Now,
MainPhotoPath = blogArticleDto.MainPhotoPath,
};
_blogArticleRepository.Add(blogArticle);
return result;
}
As you can see, "validation section" is huge and I don't want to validate my dto parameters in my service(business) layer. I want to validate "arguments" in my ioc (lightinject).
Here is my ioc code to proceed that;
public class ServiceInterceptor : IInterceptor
{
public object Invoke(IInvocationInfo invocationInfo)
{
Log.Instance.Debug("Class: ServiceInterceptor -> Method: Invoke started.");
string reflectedTypeFullname = String.Empty;
string methodName = String.Empty;
if (invocationInfo.Arguments.Any())
{
//TODO Validate method parameters here..
foreach (object argument in invocationInfo.Arguments)
{
}
}
if (invocationInfo.Method.ReflectedType != null)
{
reflectedTypeFullname = invocationInfo.Method.ReflectedType.FullName;
methodName = invocationInfo.Method.Name;
}
... ...
Now, I can take all arguments of a method to give them to my fluentvalidator. So I know I need to define typeOf argument here but after that how can I call fluent validation's related validation object* to validate argument ?
I am the author of LightInject and maybe you could see if this example works out for you.
class Program
{
static void Main(string[] args)
{
var container = new ServiceContainer();
container.Register<AbstractValidator<Foo>, FooValidator>();
container.Register<IFooService, FooService>();
container.Intercept(sr => sr.ServiceType.Name.EndsWith("Service"), factory => new ServiceInterceptior(factory));
var service = container.GetInstance<IFooService>();
service.Add(new Foo());
}
}
public interface IFooService
{
void Add(Foo foo);
}
public class FooService : IFooService
{
public void Add(Foo foo)
{
}
}
public class Foo
{
}
public class FooValidator : AbstractValidator<Foo>
{
}
public class ServiceInterceptior : IInterceptor
{
private readonly IServiceFactory factory;
public ServiceInterceptior(IServiceFactory factory)
{
this.factory = factory;
}
public object Invoke(IInvocationInfo invocationInfo)
{
foreach (var argument in invocationInfo.Arguments)
{
Type argumentType = argument.GetType();
Type validatorType = typeof (AbstractValidator<>).MakeGenericType(argumentType);
var validator = factory.TryGetInstance(validatorType);
if (validator != null)
{
var validateMethod = validatorType.GetMethod("Validate", new Type[] { argumentType });
var result = (ValidationResult)validateMethod.Invoke(validator, new object[] { argument });
if (!result.IsValid)
{
//Throw an exception, log or any other action
}
}
}
//if ok, proceed to the actual service.
return invocationInfo.Proceed();
}
}
Related
Objects are rendered as strings, (name of the object), in Application Insights custom dimensions when passed as arguments to ilogger. The actual values are not shown.
Register Application Insights
services.AddApplicationInsightsTelemetry();
New log
public class HealthController : ControllerBase
{
private readonly ILogger<HealthController> _logger;
public HealthController(ILogger<HealthController> logger)
{
_logger = logger;
}
[HttpGet]
public IActionResult Get()
{
var health = new HealthViewModel()
{
ok = false
};
_logger.LogInformation("Hlep me pls {health}", health);
return Ok(health);
}
}
Result
I do not want to this this for every log:
var health = new HealthViewModel()
{
ok = false
};
_logger.LogInformation("Hlep me pls {health}", JsonConvert.SerializeObject(health));
I tried creating a middleware for application insights but the value is still the name of the object..
Why are arguments not rendered as json?
Edit
It seems like
var health = new
{
ok = false
};
_logger.LogInformation("HEJ2 {health}", health);
works but not
var health = new HealthViewModel
{
ok = false
};
_logger.LogInformation("HEJ2 {health}", health);
Not supported
Quote from https://github.com/microsoft/ApplicationInsights-dotnet/issues/1722
I think you're expecting too much of the logger. It doesn't know about JSON format, it just calls Convert.ToString on properties
Convert.ToString typically calls ToString() and the default ToString implementation for new classes is simply to return the type name
What you can do
Use ToJson() on objects logged to ILogger and create a middleware for application insights and modify the name of the log and the custom dimensions.
Middleware
public class ProcessApiTraceFilter : ITelemetryProcessor
{
private ITelemetryProcessor Next { get; set; }
private readonly IIdentity _identity;
private readonly IHostEnvironment _hostEnvironment;
public ProcessApiTraceFilter(ITelemetryProcessor next, IHostEnvironment hostEnvironment, IIdentity identity)
{
Next = next;
_identity = identity;
_hostEnvironment = hostEnvironment;
}
public void Process(ITelemetry item)
{
item.Process(_hostEnvironment, _identity);
Next.Process(item);
}
}
Implementation
public static class ApplicationInsightsExtensions
{
public static void Process(this ITelemetry item, IHostEnvironment hostEnvironment, IIdentity identity)
{
if (item is TraceTelemetry)
{
var traceTelemetry = item as TraceTelemetry;
var originalMessage = traceTelemetry.Properties.FirstOrDefault(x => x.Key == "{OriginalFormat}");
if (!string.IsNullOrEmpty(originalMessage.Key))
{
var reg = new Regex("{([A-z]*)*}", RegexOptions.Compiled);
var match = reg.Matches(originalMessage.Value);
var formattedMessage = originalMessage.Value;
foreach (Match arg in match)
{
var parameterName = arg.Value.Replace("{", "").Replace("}", "");
var parameterValue = traceTelemetry.Properties.FirstOrDefault(x => x.Key == parameterName);
formattedMessage = formattedMessage.Replace(arg.Value, "");
}
traceTelemetry.Message = formattedMessage.Trim();
}
if (identity != null)
{
var isAuthenticated = identity.IsAuthenticated();
const string customerKey = "customer";
if (isAuthenticated && !traceTelemetry.Properties.ContainsKey(customerKey))
{
var customer = identity.Customer();
if (customer != null)
{
traceTelemetry.Properties.Add(customerKey, customer.ToJson());
}
}
var request = identity.Request();
const string requestKey = "request";
if (request != null && !traceTelemetry.Properties.ContainsKey(requestKey))
{
traceTelemetry.Properties.Add(requestKey, request.ToJson());
}
}
var applicationNameKey = "applicationName";
if (hostEnvironment != null && !string.IsNullOrEmpty(hostEnvironment.ApplicationName) && !traceTelemetry.Properties.ContainsKey(applicationNameKey))
{
traceTelemetry.Properties.Add(applicationNameKey, hostEnvironment.ApplicationName);
}
}
}
}
Register application insights and middleware in startup
services.AddApplicationInsightsTelemetry();
services.AddApplicationInsightsTelemetryProcessor<ProcessApiTraceFilter>();
ToJson
public static class ObjectExtensions
{
private static readonly string Null = "null";
private static readonly string Exception = "Could not serialize object to json";
public static string ToJson(this object value, Formatting formatting = Formatting.None)
{
if (value == null) return Null;
try
{
string json = JsonConvert.SerializeObject(value, formatting);
return json;
}
catch (Exception ex)
{
return $"{Exception} - {ex?.Message}";
}
}
}
Log
//Log object? _smtpAppSettings.ToJson()
_logger.LogInformation("Email sent {to} {from} {subject}", to, _smtpAppSettings.From, subject)
Result
from your custom dimensions i can see that it`s not considering the health obj param as an extra data
_logger.LogInformation("Hlep me pls {health}", health);
trying using the jsonConverter within the string itself.
_logger.LogInformation($"Hlep me pls {JsonConvert.SerializeObject(health)}");
I've got a setup like this with a concrete class that is instantiated inside the method I want to test. I want to mock this concrete class an not have it execute the code inside. Hence, no exception should be thrown:
public class Executor
{
public bool ExecuteAction(ActionRequest request)
{
switch (request.ActionType)
{
case ActionType.Foo:
var a = new Foo();
return a.Execute(request);
case ActionType.Bar:
var b = new Bar();
return b.Execute(request);
}
return true;
}
}
public class Foo
{
public virtual bool Execute(ActionRequest request)
{
throw new NotImplementedException();
}
}
public class Bar
{
public virtual bool Execute(ActionRequest request)
{
throw new NotImplementedException();
}
}
My NUnit test looks like this:
[Test]
public void GivenARequestToFooShouldExecuteFoo()
{
var action = new Mock<Foo>();
action.Setup(x => x.Execute(It.IsAny<ActionRequest>())).Returns(true);
var sut = new Mock<Executor>();
sut.Object.ExecuteAction(new ActionRequest
{
ActionType = ActionType.Foo
});
}
[Test]
public void GivenARequestToBarShouldExecuteBar()
{
var action = new Mock<Bar>();
action.Setup(x => x.Execute(It.IsAny<ActionRequest>())).Returns(true);
var sut = new Mock<Executor>();
sut.Object.ExecuteAction(new ActionRequest
{
ActionType = ActionType.Bar
});
}
I fiddled around with CallBase, but it didn't get me anywhere. Is there anyway I can solve this easily without dependency injection of these classes and adding interfaces? Is this possible just using Moq?
The only thing I can think to do currently is move the Execute methods into the Executor class and rename them to ExecuteFoo() and ExecuteBar(), but I have a lot of code to move so they'd have to be partial classes (sub classes?).
The problem is not with the mocking of the method but with the creation of the concrete class. The creation of Foo and Bar need to be inverted out of the Executor. It is responsible for executing the action, not creating it. with that this interface was created to handle the creation.
public interface IActionCollection : IDictionary<ActionType, Func<IExecute>> {
}
think of this as a collection of factories or a collection of creation strategies.
A common interface was created for the actions.
public interface IExecute {
bool Execute(ActionRequest request);
}
public class Foo : IExecute {
public virtual bool Execute(ActionRequest request) {
throw new NotImplementedException();
}
}
public class Bar : IExecute {
public virtual bool Execute(ActionRequest request) {
throw new NotImplementedException();
}
}
And the Executor was refactored to use dependency inversion.
public class Executor {
readonly IActionCollection factories;
public Executor(IActionCollection factories) {
this.factories = factories;
}
public bool ExecuteAction(ActionRequest request) {
if (factories.ContainsKey(request.ActionType)) {
var action = factories[request.ActionType]();
return action.Execute(request);
}
return false;
}
}
With that refactor done the Executor can be tested with fake actions.
public void GivenARequestToFooShouldExecuteFoo() {
//Arrange
var expected = true;
var key = ActionType.Foo;
var action = new Mock<Foo>();
action.Setup(x => x.Execute(It.IsAny<ActionRequest>())).Returns(expected);
var actions = new Mock<IActionCollection>();
actions.Setup(_ => _[key]).Returns(() => { return () => action.Object; });
actions.Setup(_ => _.ContainsKey(key)).Returns(true);
var sut = new Executor(actions.Object);
var request = new ActionRequest {
ActionType = ActionType.Foo
};
//Act
var actual = sut.ExecuteAction(request);
//Assert
Assert.AreEqual(expected, actual);
}
A production implementation of the factory collection can look like this
public class ActionCollection : Dictionary<ActionType, Func<IExecute>>, IActionCollection {
public ActionCollection()
: base() {
}
}
and configured accordingly with your concrete types.
var factories = ActionCollection();
factories[ActionType.Foo] = () => new Foo();
factories[ActionType.Bar] = () => new Bar();
I'm working on some legacy code (def: untested code - some well designed some not) and trying to develop some tests to confirm recent changes did what they expected etc. I'm running into an issue where I'm trying to force a method that has a try{catch} block in it to throw an exception using Moq. When I try to run the test it fails during the mock.Setup call with System.ArgumentException "Expression of type 'System.Web.Mvc.ActionResult' cannot be used for return type 'System.Web.Mvc.ActionResult'".
The basic setup of the code:
Interface for FilterController...
public interface IFilterController
{
ActionResult DeleteFilter(string reportFilter, bool customReport = true);
}
FilterController class...
public class FilterController : BaseController, IFilterController
{
public FilterController(
IServiceFactory serviceFactory,
IAwsServiceFactory awsServiceFactory,
IReportServiceFactory reportServiceFactory,
IAzureServiceFactory azureServiceFactory)
: base(typeof(FilterController), serviceFactory, awsServiceFactory, reportServiceFactory, azureServiceFactory)
{
}
// method under test
public ActionResult (string reportFilter, bool customReport = true) {
try {
// NOTE: I have trimmed down the actual code in the try block significantly for brevity - I should be able to hook onto something here as a way to mock something throwing an exception
var customReportFilterService = _serviceFactory.CreateCustomReportFilterService();
var emailReportSettingService = _serviceFactory.CreateEmailReportSettingService();
string message = string.Empty;
JsonReturnType type = JsonReturnType.DisplayMessage; // an enum
var filter = customReportFilterService.GetReportFilterByHash(SessionHelper.User.CustomerId, reportFilter, initLinkedProjects: true);
return JsonActionResult(type, ajaxMessage: message, redirectTo: filter == null ? null : string.Format("Report/{0}", filter.ReportName));
}
catch (Exception ex)
{
return JsonActionResult(JsonReturnType.Error, ajaxMessage: "There was an error in deleting the filter.");
}
}
}
BaseController class...
public class BaseController : Controller
{
private readonly ProgressController _progressController;
protected IServiceFactory _serviceFactory;
protected IAwsServiceFactory _awsServiceFactory;
protected IReportServiceFactory _reportServiceFactory;
protected IAzureServiceFactory _azureServiceFactory;
protected IApplicationSettingService _applicationSettingService;
protected IReportMonitorService _reportMonitorService;
protected ISymmetricAlgorithmProvider HiddenEncrypter { get; set; }
private Stopwatch _watch;
private bool _timePageEnabled;
private bool _maintenance;
private int _pageLoadThreshold;
private readonly ILog Logger;
public BaseController(Type type, IServiceFactory serviceFactory, IAwsServiceFactory awsServiceFactory, IReportServiceFactory reportServiceFactory, IAzureServiceFactory azureServiceFactory)
{
Logger = LogManager.GetLogger(type);
_progressController = new ProgressController();
_serviceFactory = serviceFactory;
_awsServiceFactory = awsServiceFactory;
_reportServiceFactory = reportServiceFactory;
_azureServiceFactory = azureServiceFactory;
_applicationSettingService = _serviceFactory.CreateApplicationSettingService();
_reportMonitorService = _serviceFactory.CreateReportMonitorService();
_watch = new Stopwatch();
_timePageEnabled = _applicationSettingService.ReadApplicationSettingFromCache<bool>(CC.Data.Model.Constants.ApplicationSettings.CheckSlowPageLoad, true);
_pageLoadThreshold = _applicationSettingService.ReadApplicationSettingFromCache<int>(CC.Data.Model.Constants.ApplicationSettings.PageLoadThreshold, 120);
_maintenance = _applicationSettingService.MaintenanceMode();
}
// System.Web.Mvc.ActionResult type mentioned in error
public ActionResult JsonActionResult(JsonReturnType returnType, string view = null, string ajaxMessage = null, string redirectTo = null, string target = null, object data = null, string popupTitle = null)
{
if (returnType == JsonReturnType.LoadContent)
_progressController.CompleteProgressCache();
var serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = Int32.MaxValue;
var resultData = new {
ReturnType = returnType,
HtmlView = view,
Message = ajaxMessage,
RedirectTo = redirectTo,
Target = target,
CustomData = data,
ProjectId = SessionHelper.ProjectId,
PopupTitle = popupTitle,
MaintenanceMode = _maintenance
};
ContentResult result;
result = new ContentResult
{
Content = serializer.Serialize(resultData),
ContentType = "application/json"
};
return result;
}
}
Controller class...
public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter {
// stuff
}
Unit Test class...
[TestClass]
public class FilterControllerTest
{
private FilterController filterController;
private Mock<IFilterController> filterControllerMock;
private Mock<IServiceFactory> serviceFactoryMock;
private Mock<IAwsServiceFactory> awsServiceFactoryMock;
private Mock<IReportServiceFactory> reportServiceFactoryMock;
private Mock<IAzureServiceFactory> azureServiceFactoryMock;
private Mock<IApplicationSettingService> applicationSettingServiceMock;
[ClassInitialize]
public static void ClassInit(TestContext context)
{
}
[TestInitialize]
public void Initialize()
{
filterControllerMock = new Mock<IFilterController>();
serviceFactoryMock = new Mock<IServiceFactory>();
awsServiceFactoryMock = new Mock<IAwsServiceFactory>();
reportServiceFactoryMock = new Mock<IReportServiceFactory>();
azureServiceFactoryMock = new Mock<IAzureServiceFactory>();
applicationSettingServiceMock = new Mock<IApplicationSettingService>();
serviceFactoryMock
.Setup(s => s.CreateApplicationSettingService())
.Returns(applicationSettingServiceMock.Object);
filterController = new FilterController(
serviceFactoryMock.Object
, awsServiceFactoryMock.Object
, reportServiceFactoryMock.Object
, azureServiceFactoryMock.Object);
}
[TestCleanup]
public void Cleanup()
{
}
[ExpectedException(typeof(Exception))]
[TestMethod]
public void DeleteFilter_ExceptionThrown_IsCaughtAndLoggedAndReturnsActionResultOfError()
{
// Arrange
filterControllerMock
.Setup(x => x.DeleteFilter(It.IsAny<string>(), It.IsAny<bool>()))
.Throws(new Exception());
// Act
var result = filterController.DeleteFilter("myfilt", false);
}
}
In the end all I want to do is have it so when DeleteFilter is called for this test, an error is thrown and then I can assert what is returned from the catch block.
EDIT: have majorly updated the post by suggestion to make it easier to understand where the issue is.
Here is a slimmed down example
Given
public interface IServiceFactory {
object GetService(string args);
}
public class MyController : Controller {
IServiceFactory serviceFactory
public MyController(IServiceFactory serviceFactory) {
this.serviceFactory = serviceFactory;
}
// method under test
public ActionResult DeleteFilter(string args) {
try {
var model = serviceFactory.GetService(args);
return View(model);
} catch(Exception ex) {
return JsonActionResult(JsonReturnType.Error, ajaxMessage: "There was an error in deleting the filter.");
}
}
}
You can use moq in your test like this
[TestMethod]
public void DeleteFilter_ExceptionThrown_IsCaughtAndLoggedAndReturnsActionResultOfError() {
// Arrange
var serviceFactoryMock = new Mock<IServiceFactory>();
serviceFactoryMock
.Setup(x => x.GetService(It.IsAny<string>())
.Throws(new Exception())
.Verifiable();
var controller = new MyController(serviceFactoryMock.Object);
// Act
var result = controller.DeleteFilter("blah blah");
//Assert
serviceFactoryMock.Verify(); // verifies that the setup was invoked
Assert.IsNotNull(result);
Assert.IsInstanceOfType(result, typeof(JsonActionResult));
//...other assertions
}
So now in the example when DeleteFilter is called, the mock service factory is invoked, an error is thrown based on the setup and you can assert what is returned from the catch block.
there have been quite some posts about this, all trying to serialize a Func delegate.
But could someone think of an alternative, when the use of the delegate is always clear?
We have a generic create command, which takes a delegate as paramater in the constructor. This delegate will create the Item for the create command:
public class CreateCommand<T> : Command
{
public T Item;
protected Func<T> Constructor;
public ClientCreateCommand(Func<T> constructor)
{
Constructor = constructor;
}
public override void Execute()
{
Item = Constructor();
}
}
The command is used like this:
var c = new CreateCommand<MyType>( () => Factory.CreateMyType(param1, param2, ...) );
History.Insert(c);
Then the History serializes the command and sends it to the server. ofc the delegate can't be serialized as is and we get an exception.
Now could someone think of a very simple Constructor class that can be serialized and does the same job than the lambda expresseion? Means it takes a list of paramters and returns an instance of type T, that we then can write somethink like this:
var constructor = new Constructor<MyType>(param1, param2, ...);
var c = new CreateCommand<MyType>(constructor);
History.Insert(c);
How would the Constructor class look like? Thanks for any ideas!
EDIT(2): I've provided a couple of complete example implementations. They are categorized below as "Implementation 1" and "Implementation 2".
Your delegate is essentially a factory. You could define a factory interface and create a class that implements that interface for your Item class. Below is an example:
public interface IFactory<T>
{
T Create();
}
[Serializable]
public class ExampleItemFactory : IFactory<T>
{
public int Param1 { get; set; }
public string Param2 { get; set; }
#region IFactory<T> Members
public Item Create()
{
return new Item(this.Param1, this.Param2);
}
#endregion
}
public class CreateCommand<T> : Command
{
public T Item;
protected IFactory<T> _ItemFactory;
public CreateCommand(IFactory<T> factory)
{
_ItemFactory = factory;
}
public override void Execute()
{
Item = _ItemFactory.Create();
}
}
You would utilize this code in the following manner:
IFactory<Item> itemFactory = new ExampleItemFactory { Param1 = 5, Param2 = "Example!" };
CreateCommand<Item> command = new CreateCommand<Item>(itemFactory);
command.Execute();
EDIT(1): The specific implementations of IFactory<T> that your application needs will be up to you. You could create specific factory classes for each class that you need, or you could create some kind of factory that dynamically creates an instance using, for example, the Activator.CreateInstance function or perhaps using some kind of Inversion of Control framework such as Spring or StructureMap.
Below is a complete example implementation that uses two factory implementations. One implementation can create any type given an array of arguments using that type's constructor with matching parameters. Another implementation creates any type that has been registered with my "Factory" class.
The Debug.Assert statements ensure that everything is behaving as intended. I ran this application without error.
Implementation 1
[Serializable]
public abstract class Command
{
public abstract void Execute();
}
public class Factory
{
static Dictionary<Type, Func<object[], object>> _DelegateCache = new Dictionary<Type, Func<object[], object>>();
public static void Register<T>(Func<object[], object> #delegate)
{
_DelegateCache[typeof(T)] = #delegate;
}
public static T CreateMyType<T>(params object[] args)
{
return (T)_DelegateCache[typeof(T)](args);
}
}
public interface IFactory<T>
{
T Create();
}
[Serializable]
public class CreateCommand<T> : Command
{
public T Item { get; protected set; }
protected IFactory<T> _ItemFactory;
public CreateCommand(IFactory<T> itemFactory)
{
this._ItemFactory = itemFactory;
}
public override void Execute()
{
this.Item = this._ItemFactory.Create();
}
}
// This class is a base class that represents a factory capable of creating an instance using a dynamic set of arguments.
[Serializable]
public abstract class DynamicFactory<T> : IFactory<T>
{
public object[] Args { get; protected set; }
public DynamicFactory(params object[] args)
{
this.Args = args;
}
public DynamicFactory(int numberOfArgs)
{
if (numberOfArgs < 0)
throw new ArgumentOutOfRangeException("numberOfArgs", "The numberOfArgs parameter must be greater than or equal to zero.");
this.Args = new object[numberOfArgs];
}
#region IFactory<T> Members
public abstract T Create();
#endregion
}
// This implementation uses the Activator.CreateInstance function to create an instance
[Serializable]
public class DynamicConstructorFactory<T> : DynamicFactory<T>
{
public DynamicConstructorFactory(params object[] args) : base(args) { }
public DynamicConstructorFactory(int numberOfArgs) : base(numberOfArgs) { }
public override T Create()
{
return (T)Activator.CreateInstance(typeof(T), this.Args);
}
}
// This implementation uses the Factory.CreateMyType function to create an instance
[Serializable]
public class MyTypeFactory<T> : DynamicFactory<T>
{
public MyTypeFactory(params object[] args) : base(args) { }
public MyTypeFactory(int numberOfArgs) : base(numberOfArgs) { }
public override T Create()
{
return Factory.CreateMyType<T>(this.Args);
}
}
[Serializable]
class DefaultConstructorExample
{
public DefaultConstructorExample()
{
}
}
[Serializable]
class NoDefaultConstructorExample
{
public NoDefaultConstructorExample(int a, string b, float c)
{
}
}
[Serializable]
class PrivateConstructorExample
{
private int _A;
private string _B;
private float _C;
private PrivateConstructorExample()
{
}
public static void Register()
{
// register a delegate with the Factory class that will construct an instance of this class using an array of arguments
Factory.Register<PrivateConstructorExample>((args) =>
{
if (args == null || args.Length != 3)
throw new ArgumentException("Expected 3 arguments.", "args");
if (!(args[0] is int))
throw new ArgumentException("First argument must be of type System.Int32.", "args[0]");
if (!(args[1] is string))
throw new ArgumentException("Second argument must be of type System.String.", "args[1]");
if (!(args[2] is float))
throw new ArgumentException("Third argument must be of type System.Single.", "args[2]");
var instance = new PrivateConstructorExample();
instance._A = (int)args[0];
instance._B = (string)args[1];
instance._C = (float)args[2];
return instance;
});
}
}
class Program
{
static void Main(string[] args)
{
var factory1 = new DynamicConstructorFactory<DefaultConstructorExample>(null);
var command1 = new CreateCommand<DefaultConstructorExample>(factory1);
var factory2 = new DynamicConstructorFactory<NoDefaultConstructorExample>(3);
factory2.Args[0] = 5;
factory2.Args[1] = "ABC";
factory2.Args[2] = 7.1f;
var command2 = new CreateCommand<NoDefaultConstructorExample>(factory2);
PrivateConstructorExample.Register(); // register this class so that it can be created by the Factory.CreateMyType function
var factory3 = new MyTypeFactory<PrivateConstructorExample>(3);
factory3.Args[0] = 5;
factory3.Args[1] = "ABC";
factory3.Args[2] = 7.1f;
var command3 = new CreateCommand<PrivateConstructorExample>(factory3);
VerifySerializability<DefaultConstructorExample>(command1);
VerifySerializability<NoDefaultConstructorExample>(command2);
VerifySerializability<PrivateConstructorExample>(command3);
}
static void VerifySerializability<T>(CreateCommand<T> originalCommand)
{
var serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
using (var stream = new System.IO.MemoryStream())
{
System.Diagnostics.Debug.Assert(originalCommand.Item == null); // assert that originalCommand does not yet have a value for Item
serializer.Serialize(stream, originalCommand); // serialize the originalCommand object
stream.Seek(0, System.IO.SeekOrigin.Begin); // reset the stream position to the beginning for deserialization
// deserialize
var deserializedCommand = serializer.Deserialize(stream) as CreateCommand<T>;
System.Diagnostics.Debug.Assert(deserializedCommand.Item == null); // assert that deserializedCommand still does not have a value for Item
deserializedCommand.Execute();
System.Diagnostics.Debug.Assert(deserializedCommand.Item != null); // assert that deserializedCommand now has a value for Item
}
}
}
EDIT(2): After re-reading the question, I think I got a better idea of what the asker was really trying to get at. Essentially, we still want to take advantage of the flexibility offered by lambda expressions / anonymous delegates, but avoid the serialization issues.
Below is another example implementation that utilizes a Factory<T> class to store delegates used to return instances of type T.
Implementation 2
[Serializable]
public abstract class Command
{
public abstract void Execute();
}
[Serializable]
public abstract class CreateCommand<T> : Command
{
public T Item { get; protected set; }
}
public class Factory<T>
{
private static readonly object _SyncLock = new object();
private static Func<T> _CreateFunc;
private static Dictionary<string, Func<T>> _CreateFuncDictionary;
/// <summary>
/// Registers a default Create Func delegate for type <typeparamref name="T"/>.
/// </summary>
public static void Register(Func<T> createFunc)
{
lock (_SyncLock)
{
_CreateFunc = createFunc;
}
}
public static T Create()
{
lock (_SyncLock)
{
if(_CreateFunc == null)
throw new InvalidOperationException(string.Format("A [{0}] delegate must be registered as the default delegate for type [{1}]..", typeof(Func<T>).FullName, typeof(T).FullName));
return _CreateFunc();
}
}
/// <summary>
/// Registers a Create Func delegate for type <typeparamref name="T"/> using the given key.
/// </summary>
/// <param name="key"></param>
/// <param name="createFunc"></param>
public static void Register(string key, Func<T> createFunc)
{
lock (_SyncLock)
{
if (_CreateFuncDictionary == null)
_CreateFuncDictionary = new Dictionary<string, Func<T>>();
_CreateFuncDictionary[key] = createFunc;
}
}
public static T Create(string key)
{
lock (_SyncLock)
{
Func<T> createFunc;
if (_CreateFuncDictionary != null && _CreateFuncDictionary.TryGetValue(key, out createFunc))
return createFunc();
else
throw new InvalidOperationException(string.Format("A [{0}] delegate must be registered with the given key \"{1}\".", typeof(Func<T>).FullName, key));
}
}
}
[Serializable]
public class CreateCommandWithDefaultDelegate<T> : CreateCommand<T>
{
public override void Execute()
{
this.Item = Factory<T>.Create();
}
}
[Serializable]
public class CreateCommandWithKeyedDelegate<T> : CreateCommand<T>
{
public string CreateKey { get; set; }
public CreateCommandWithKeyedDelegate(string createKey)
{
this.CreateKey = createKey;
}
public override void Execute()
{
this.Item = Factory<T>.Create(this.CreateKey);
}
}
[Serializable]
class DefaultConstructorExample
{
public DefaultConstructorExample()
{
}
}
[Serializable]
class NoDefaultConstructorExample
{
public NoDefaultConstructorExample(int a, string b, float c)
{
}
}
[Serializable]
class PublicPropertiesExample
{
public int A { get; set; }
public string B { get; set; }
public float C { get; set; }
}
class Program
{
static void Main(string[] args)
{
// register delegates for each type
Factory<DefaultConstructorExample>.Register(() => new DefaultConstructorExample());
Factory<NoDefaultConstructorExample>.Register(() => new NoDefaultConstructorExample(5, "ABC", 7.1f));
Factory<PublicPropertiesExample>.Register(() => new PublicPropertiesExample() { A = 5, B = "ABC", C = 7.1f });
// create commands
var command1 = new CreateCommandWithDefaultDelegate<DefaultConstructorExample>();
var command2 = new CreateCommandWithDefaultDelegate<DefaultConstructorExample>();
var command3 = new CreateCommandWithDefaultDelegate<DefaultConstructorExample>();
// verify that each command can be serialized/deserialized and that the creation logic works
VerifySerializability<DefaultConstructorExample>(command1);
VerifySerializability<DefaultConstructorExample>(command2);
VerifySerializability<DefaultConstructorExample>(command3);
// register additional delegates for each type, distinguished by key
Factory<DefaultConstructorExample>.Register("CreateCommand", () => new DefaultConstructorExample());
Factory<NoDefaultConstructorExample>.Register("CreateCommand", () => new NoDefaultConstructorExample(5, "ABC", 7.1f));
Factory<PublicPropertiesExample>.Register("CreateCommand", () => new PublicPropertiesExample() { A = 5, B = "ABC", C = 7.1f });
// create commands, passing in the create key to the constructor
var command4 = new CreateCommandWithKeyedDelegate<DefaultConstructorExample>("CreateCommand");
var command5 = new CreateCommandWithKeyedDelegate<DefaultConstructorExample>("CreateCommand");
var command6 = new CreateCommandWithKeyedDelegate<DefaultConstructorExample>("CreateCommand");
// verify that each command can be serialized/deserialized and that the creation logic works
VerifySerializability<DefaultConstructorExample>(command4);
VerifySerializability<DefaultConstructorExample>(command5);
VerifySerializability<DefaultConstructorExample>(command6);
}
static void VerifySerializability<T>(CreateCommand<T> originalCommand)
{
var serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
using (var stream = new System.IO.MemoryStream())
{
System.Diagnostics.Debug.Assert(originalCommand.Item == null); // assert that originalCommand does not yet have a value for Item
serializer.Serialize(stream, originalCommand); // serialize the originalCommand object
stream.Seek(0, System.IO.SeekOrigin.Begin); // reset the stream position to the beginning for deserialization
// deserialize
var deserializedCommand = serializer.Deserialize(stream) as CreateCommand<T>;
System.Diagnostics.Debug.Assert(deserializedCommand.Item == null); // assert that deserializedCommand still does not have a value for Item
deserializedCommand.Execute();
System.Diagnostics.Debug.Assert(deserializedCommand.Item != null); // assert that deserializedCommand now has a value for Item
}
}
}
Is there any way to pass generic types using a TestCase to a test in NUnit?
This is what I would like to do but the syntax is not correct...
[Test]
[TestCase<IMyInterface, MyConcreteClass>]
public void MyMethod_GenericCall_MakesGenericCall<TInterface, TConcreteClass>()
{
// Arrange
// Act
var response = MyClassUnderTest.MyMethod<TInterface>();
// Assert
Assert.IsInstanceOf<TConcreteClass>(response);
}
Or if not, what is the best way to achieve the same functionality (obviously I'll have multiple TestCases in the real code)?
Update with another example...
Here is another example with a single generic type passed...
[Test]
[TestCase<MyClass>("Some response")]
public void MyMethod_GenericCall_MakesGenericCall<T>(string expectedResponse)
{
// Arrange
// Act
var response = MyClassUnderTest.MyMethod<T>();
// Assert
Assert.AreEqual(expectedResponse, response);
}
NUnit test methods actually can be generic as long as the generic type arguments can be inferred from parameters:
[TestCase(42)]
[TestCase("string")]
[TestCase(double.Epsilon)]
public void GenericTest<T>(T instance)
{
Console.WriteLine(instance);
}
If the generic arguments cannot be inferred, the test runner will not have a clue how to resolve type arguments:
[TestCase(42)]
[TestCase("string")]
[TestCase(double.Epsilon)]
public void GenericTest<T>(object instance)
{
Console.WriteLine(instance);
}
But for this case you can implement a custom attribute:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class TestCaseGenericAttribute : TestCaseAttribute, ITestBuilder
{
public TestCaseGenericAttribute(params object[] arguments)
: base(arguments)
{
}
public Type[] TypeArguments { get; set; }
IEnumerable<TestMethod> ITestBuilder.BuildFrom(IMethodInfo method, Test suite)
{
if (!method.IsGenericMethodDefinition)
return base.BuildFrom(method, suite);
if (TypeArguments == null || TypeArguments.Length != method.GetGenericArguments().Length)
{
var parms = new TestCaseParameters { RunState = RunState.NotRunnable };
parms.Properties.Set(PropertyNames.SkipReason, $"{nameof(TypeArguments)} should have {method.GetGenericArguments().Length} elements");
return new[] { new NUnitTestCaseBuilder().BuildTestMethod(method, suite, parms) };
}
var genMethod = method.MakeGenericMethod(TypeArguments);
return base.BuildFrom(genMethod, suite);
}
}
Usage:
[TestCaseGeneric("Some response", TypeArguments = new[] { typeof(IMyInterface), typeof(MyConcreteClass) }]
public void MyMethod_GenericCall_MakesGenericCall<T1, T2>(string expectedResponse)
{
// whatever
}
And a similar customization for TestCaseSourceAttribute:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class TestCaseSourceGenericAttribute : TestCaseSourceAttribute, ITestBuilder
{
public TestCaseSourceGenericAttribute(string sourceName)
: base(sourceName)
{
}
public Type[] TypeArguments { get; set; }
IEnumerable<TestMethod> ITestBuilder.BuildFrom(IMethodInfo method, Test suite)
{
if (!method.IsGenericMethodDefinition)
return base.BuildFrom(method, suite);
if (TypeArguments == null || TypeArguments.Length != method.GetGenericArguments().Length)
{
var parms = new TestCaseParameters { RunState = RunState.NotRunnable };
parms.Properties.Set(PropertyNames.SkipReason, $"{nameof(TypeArguments)} should have {method.GetGenericArguments().Length} elements");
return new[] { new NUnitTestCaseBuilder().BuildTestMethod(method, suite, parms) };
}
var genMethod = method.MakeGenericMethod(TypeArguments);
return base.BuildFrom(genMethod, suite);
}
}
Usage:
[TestCaseSourceGeneric(nameof(mySource)), TypeArguments = new[] { typeof(IMyInterface), typeof(MyConcreteClass) }]
Update for C# 11.0:
Starting with C# 11.0 you can specify generic attributes. This makes possible to use generic [TestCase<...>] attributes exactly the same way as the OP wanted:
// Requires C# 11.
// For exactly one type argument. See the base implementation above.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class TestCaseAttribute<T> : TestCaseGenericAttribute
{
public TestCaseAttribute(params object[] arguments)
: base(arguments) => TypeArguments = new[] { typeof(T) };
}
// For exactly two type arguments. See the base implementation above.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class TestCaseAttribute<T1, T2> : TestCaseGenericAttribute
{
public TestCaseAttribute(params object[] arguments)
: base(arguments) => TypeArguments = new[] { typeof(T1), typeof(T2) };
}
// You can add more classes to support more type arguments or
// to create specialized [TestCaseSource<...>] attributes the same way.
So finally, this is now supported:
[TestCase<IMyInterface, MyConcreteClass>("Some response")]
public void MyMethod_GenericCall_MakesGenericCall<T1, T2>(string expectedResponse)
{
// whatever
}
I had occasion to do something similar today, and wasn't happy with using reflection.
I decided to leverage [TestCaseSource] instead by delegating the test logic as a test context to a generic testing class, pinned on a non-generic interface, and called the interface from individual tests (my real tests have many more methods in the interface, and use AutoFixture to set up the context):
class Sut<T>
{
public string ReverseName()
{
return new string(typeof(T).Name.Reverse().ToArray());
}
}
[TestFixture]
class TestingGenerics
{
public static IEnumerable<ITester> TestCases()
{
yield return new Tester<string> { Expectation = "gnirtS"};
yield return new Tester<int> { Expectation = "23tnI" };
yield return new Tester<List<string>> { Expectation = "1`tsiL" };
}
[TestCaseSource("TestCases")]
public void TestReverse(ITester tester)
{
tester.TestReverse();
}
public interface ITester
{
void TestReverse();
}
public class Tester<T> : ITester
{
private Sut<T> _sut;
public string Expectation { get; set; }
public Tester()
{
_sut=new Sut<T>();
}
public void TestReverse()
{
Assert.AreEqual(Expectation,_sut.ReverseName());
}
}
}
You can make custom GenericTestCaseAttribute
[Test]
[GenericTestCase(typeof(MyClass) ,"Some response", TestName = "Test1")]
[GenericTestCase(typeof(MyClass1) ,"Some response", TestName = "Test2")]
public void MapWithInitTest<T>(string expectedResponse)
{
// Arrange
// Act
var response = MyClassUnderTest.MyMethod<T>();
// Assert
Assert.AreEqual(expectedResponse, response);
}
Here is implementation of GenericTestCaseAttribute
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class GenericTestCaseAttribute : TestCaseAttribute, ITestBuilder
{
private readonly Type _type;
public GenericTestCaseAttribute(Type type, params object[] arguments) : base(arguments)
{
_type = type;
}
IEnumerable<TestMethod> ITestBuilder.BuildFrom(IMethodInfo method, Test suite)
{
if (method.IsGenericMethodDefinition && _type != null)
{
var gm = method.MakeGenericMethod(_type);
return BuildFrom(gm, suite);
}
return BuildFrom(method, suite);
}
}
Attributes in C# cannot be generic, so you won't be able to do things exactly as you'd like. Perhaps the easiest thing would be to put TestCase attributes onto a helper method which uses reflection to call the real method. Something like this might work (note, untested):
[TestCase(typeof(MyClass), "SomeResponse")]
public void TestWrapper(Type t, string s)
{
typeof(MyClassUnderTest).GetMethod("MyMethod_GenericCall_MakesGenericCall").MakeGenericMethod(t).Invoke(null, new [] { s });
}
Start with the test first--even when testing. What do you want to do? Probably something like this:
[Test]
public void Test_GenericCalls()
{
MyMethod_GenericCall_MakesGenericCall<int>("an int response");
MyMethod_GenericCall_MakesGenericCall<string>("a string response");
:
}
Then you can just make your test a plain old function test. No [Test] marker.
public void MyMethod_GenericCall_MakesGenericCall<T>(string expectedResponse)
{
// Arrange
// Act
var response = MyClassUnderTest.MyMethod<T>();
// Assert
Assert.AreEqual(expectedResponse, response);
}
I did something similar last week. Here's what I ended up with:
internal interface ITestRunner
{
void RunTest(object _param, object _expectedValue);
}
internal class TestRunner<T> : ITestRunner
{
public void RunTest(object _param, T _expectedValue)
{
T result = MakeGenericCall<T>();
Assert.AreEqual(_expectedValue, result);
}
public void RunTest(object _param, object _expectedValue)
{
RunTest(_param, (T)_expectedValue);
}
}
And then the test itself:
[Test]
[TestCase(typeof(int), "my param", 20)]
[TestCase(typeof(double), "my param", 123.456789)]
public void TestParse(Type _type, object _param, object _expectedValue)
{
Type runnerType = typeof(TestRunner<>);
var runner = Activator.CreateInstance(runnerType.MakeGenericType(_type));
((ITestRunner)runner).RunTest(_param, _expectedValue);
}
As might be testing with generic functions that return objects?. Example:
public Empleado TestObjetoEmpleado(Empleado objEmpleado)
{
return objEmpleado;
}
Thanks
I slightly modified the TestCaseGenericAttribute somebody posted here:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class GenericTestCaseAttribute : TestCaseAttribute, ITestBuilder
{
public GenericTestCaseAttribute(params object[] arguments)
: base(arguments)
{
}
IEnumerable<TestMethod> ITestBuilder.BuildFrom(IMethodInfo method, Test suite)
{
if (!method.IsGenericMethodDefinition) return base.BuildFrom(method, suite);
var numberOfGenericArguments = method.GetGenericArguments().Length;
var typeArguments = Arguments.Take(numberOfGenericArguments).OfType<Type>().ToArray();
if (typeArguments.Length != numberOfGenericArguments)
{
var parms = new TestCaseParameters { RunState = RunState.NotRunnable };
parms.Properties.Set("_SKIPREASON", $"Arguments should have {typeArguments} type elements");
return new[] { new NUnitTestCaseBuilder().BuildTestMethod(method, suite, parms) };
}
var genMethod = method.MakeGenericMethod(typeArguments);
return new TestCaseAttribute(Arguments.Skip(numberOfGenericArguments).ToArray()).BuildFrom(genMethod, suite);
}
}
This version expects one list of all parameters, starting with the type parameters, starting with the type paramters. Usage:
[Test]
[GenericTestCase(typeof(IMailService), typeof(MailService))]
[GenericTestCase(typeof(ILogger), typeof(Logger))]
public void ValidateResolution<TQuery>(Type type)
{
// arrange
var sut = new AutoFacMapper();
// act
sut.RegisterMappings();
var container = sut.Build();
// assert
var item = sut.Container.Resolve<TQuery>();
Assert.AreEqual(type, item.GetType());
}
I have written my own TestCaseGenericAttribute and TestCaseGenericSourceAttribute.
https://github.com/nunit/nunit/issues/3580