In one of my questions I used a ValidationContext
class that looked like this:
public class ValidationContext<TArg>
{
public ValidationContext(TArg argument, string memberName)
{
Argument = argument;
MemberName = memberName;
}
public TArg Argument { get; }
public string MemberName { get; }
public virtual ValidationContext<TArg> ThrowIf<TException>(Func<TArg, bool> predicate, params object[] args) where TException : Exception
{
if (predicate(Argument))
{
throw (TException)Activator.CreateInstance(typeof(TException), args);
}
return this;
}
}
Nothing special there. Just a couple of properties and one simple method.
Later I decided to create another context class for unit testing so I derived it from the first one and the result was:
public class UnitTestingValidationContext<TArg> : ValidationContext<TArg>
{
public UnitTestingValidationContext(TArg argument, string memberName)
:base(argument, memberName)
{
}
public override ValidationContext<TArg> ThrowIf<TException>(Func<TArg, bool> predicate, params object[] args)
{
try
{
base.ThrowIf<TException>(predicate, args);
return this;
}
catch (Exception inner)
{
//throw new AssertFailedException
throw new Exception("This is a test.", inner);
}
}
}
This one is very simple too. New here is the try/catch
around the base.ThrowIf
.
Then I was reading about design patterns and I thought this might be good opportunity to try out the decorator pattern. I'm not very fond of the general terminology like Component, ComponentDecorator, ConcreteDecorater and whatsoever. I'm never sure what is what so I wanted to use a real example for that.
First I created an interface:
public interface IValidationContext<TArg>
{
TArg Argument { get; }
string MemberName { get; }
IValidationContext<TArg> ThrowIf<TException>(Func<TArg, bool> predicate, params object[] args)
where TException : Exception;
}
and derived the first context from it:
public class ValidationContext<TArg> : IValidationContext<TArg>
{
public ValidationContext(TArg argument, string memberName)
{
Argument = argument;
MemberName = memberName;
}
public TArg Argument { get; }
public string MemberName { get; }
public IValidationContext<TArg> ThrowIf<TException>(Func<TArg, bool> predicate, params object[] args)
where TException : Exception
{
if (predicate(Argument))
{
throw (TException)Activator.CreateInstance(typeof(TException), args);
}
return this;
}
}
then I used the same interface for the other context but I pass the first context as a parameter to the second one:
public class UnitTestingValidationContext<TArg> : IValidationContext<TArg>
{
private readonly IValidationContext<TArg> _context;
public UnitTestingValidationContext(IValidationContext<TArg> context)
{
_context = context;
}
public TArg Argument => _context.Argument;
public string MemberName => _context.MemberName;
public IValidationContext<TArg> ThrowIf<TException>(Func<TArg, bool> predicate, params object[] args)
where TException : Exception
{
try
{
_context.ThrowIf<TException>(predicate, args);
return this;
}
catch (Exception inner)
{
//throw new AssertFailedException // does not work in linqpad
throw new Exception("This is a test.", inner);
}
}
}
Would you say this is a correct implementation of the decorator pattern?
AssertFailedException
... at least that's what I believe unit-tests have to do. – t3chb0t Jun 15 at 8:57UnitTesingValidationContext
only in unit tests or also in prodcution code? Could you pls add an example how to use it? – JanDotNet Jun 15 at 9:09UnitTestingValidationContext
is defined in a separate assembly (there is an example - one line - in my previous question here) that I add as reference in a unit test project. I wouldn't use it in production code because I requries theMicrosoft.VisualStudio.QualityTools.UnitTestFramework.dll
and I don't want to polute the production project with it. It's purpose is to use the same framework and extensions for normal validation and for unit testing. – t3chb0t Jun 15 at 10:27.Validate()
and a test with.Test()
all other validations stay the same and of course the unit test context throws theAssertFailedException
with the original exception as the inner one. – t3chb0t Jun 15 at 10:29