-
Notifications
You must be signed in to change notification settings - Fork 0
Customization
If you prefer TestFixture and Test as an attribute of a fixture, you can use them by defining them that inherit FixtureAttribute as follows.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class TestFixtureAttribute : FixtureAttribute
{
public override bool IsContainerFixture => true;
public TestFixtureAttribute() : this(null)
{
}
public TestFixtureAttribute(string description) : base(description)
{
IsRootFixture = true;
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class TestAttribute : FixtureAttribute
{
public override bool IsContainerFixture => false;
public TestAttribute() : this(null)
{
}
public TestAttribute(string description) : base(description)
{
}
}
[TestFixture]
class SampleTest
{
[Test]
void Test1()
{
}
}
Carna does not have an attribute that indicates that a fixture should not to be run like IgnoreAttribute. If you need it, you can define as follows.
At first, define IgnoreAttribute.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class IgnoreAttribute : Attribute
{
}
Next, define a finder that implements IFixtureTypeFinder and a builder that implements IFixtureBuilder to exclude a fixture specified by IgnoreAttribute.
public class CustomFixtureTypeFinder : FixtureTypeFinder
{
protected override Func<TypeInfo, bool> FixtureTypeFilter => typeInfo =>
base.FixtureTypeFilter(typeInfo) && typeInfo.GetCustomAttribute<IgnoreAttribute>() == null;
}
public class CustomFixtureBuilder : FixtureBuilder
{
protected override Func<MethodInfo, bool> MethodFilter =>
m => base.MethodFilter(m) && m.GetCustomAttribute<IgnoreAttribute>() == null;
}
Finally, speficy them in carna-runner-settings.json so that they can be used.
{
"finder": {
"type": "SampleSpec.CustomFixtureTypeFinder,SampleSpec"
},
"builder": {
"type": "SampleSpec.CustomFixtureBuilder,SampleSpec"
}
}
[Specification]
class CustomerSpec
{
[Example]
[Ignore]
void Ex01()
{
}
[Example]
void Ex02()
{
}
}
If you prefer Arrange, Act and Assert as a step, you can use them by defining them as follows.
At first, define ArranStep, ActStep, and AssertStep that inherits FixtureStep.
public class ArrangeStep : FixtureStep
{
public Action Arrangement { get; }
public ArrangeStep(string description, Type callerType, string callerMemberName, string callerFilePath, int callerLineNumber) : base(description, callerType, callerMemberName, callerFilePath, callerLineNumber)
{
}
public ArrangeStep(string description, Action arrangement, Type callerType, string callerMemberName, string callerFilePath, int callerLineNumber) : base(description, callerType, callerMemberName, callerFilePath, callerLineNumber)
{
Arrangement = arrangement;
}
}
public class ActStep : FixtureStep
{
public Action Action { get; }
public ActStep(string description, Type callerType, string callerMemberName, string callerFilePath, int callerLineNumber) : base(description, callerType, callerMemberName, callerFilePath, callerLineNumber)
{
}
public ActStep(string description, Action action, Type callerType, string callerMemberName, string callerFilePath, int callerLineNumber) : base(description, callerType, callerMemberName, callerFilePath, callerLineNumber)
{
Action = action;
}
}
public class AssertStep : FixtureStep
{
public Expression<Func<bool>> Assertion { get; }
public Expression<Func<Exception, bool>> ExceptionAssertion { get; }
public Action Action { get; }
public Action<Exception> ExceptionAction { get; }
public AssertStep(string description, Type callerType, string callerMemberName, string callerFilePath, int callerLineNumber) : base(description, callerType, callerMemberName, callerFilePath, callerLineNumber)
{
}
public AssertStep(string description, Expression<Func<bool>> assertion, Type callerType, string callerMemberName, string callerFilePath, int callerLineNumber) : base(description, callerType, callerMemberName, callerFilePath, callerLineNumber)
{
Assertion = assertion;
}
public AssertStep(string description, Expression<Func<Exception, bool>> exceptionAssertion, Type callerType, string callerMemberName, string callerFilePath, int callerLineNumber) : base(description, callerType, callerMemberName, callerFilePath, callerLineNumber)
{
ExceptionAssertion = exceptionAssertion;
}
public AssertStep(string description, Action action, Type callerType, string callerMemberName, string callerFilePath, int callerLineNumber) : base(description, callerType, callerMemberName, callerFilePath, callerLineNumber)
{
Action = action;
}
public AssertStep(string description, Action<Exception> exceptionAction, Type callerType, string callerMemberName, string callerFilePath, int callerLineNumber) : base(description, callerType, callerMemberName, callerFilePath, callerLineNumber)
{
ExceptionAction = exceptionAction;
}
}
Next, define a class that implements IFixtureSteppable so that these steps can be used.
public class ArrangeActAssertSteppable : IFixtureSteppable
{
protected virtual void Arrange(string description, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0)
{
(this as IFixtureSteppable).Stepper.Take(new ArrangeStep(description, GetType(), callerMemberName, callerFilePath, callerLineNumber));
}
protected virtual void Arrange(string description, Action arrangement, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0)
{
(this as IFixtureSteppable).Stepper.Take(new ArrangeStep(description, arrangement, GetType(), callerMemberName, callerFilePath, callerLineNumber));
}
protected virtual void Act(string description, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0)
{
(this as IFixtureSteppable).Stepper.Take(new ActStep(description, GetType(), callerMemberName, callerFilePath, callerLineNumber));
}
protected virtual void Act(string description, Action action, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0)
{
(this as IFixtureSteppable).Stepper.Take(new ActStep(description, action, GetType(), callerMemberName, callerFilePath, callerLineNumber));
}
protected virtual void Assert(string description, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0)
{
(this as IFixtureSteppable).Stepper.Take(new AssertStep(description, GetType(), callerMemberName, callerFilePath, callerLineNumber));
}
protected virtual void Assert(string description, Expression<Func<bool>> assertion, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0)
{
(this as IFixtureSteppable).Stepper.Take(new AssertStep(description, assertion, GetType(), callerMemberName, callerFilePath, callerLineNumber));
}
protected virtual void Assert(string description, Expression<Func<Exception, bool>> exceptionAssertion, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0)
{
(this as IFixtureSteppable).Stepper.Take(new AssertStep(description, exceptionAssertion, GetType(), callerMemberName, callerFilePath, callerLineNumber));
}
protected virtual void Assert(string description, Action assertion, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0)
{
(this as IFixtureSteppable).Stepper.Take(new AssertStep(description, assertion, GetType(), callerMemberName, callerFilePath, callerLineNumber));
}
protected virtual void Assert(string description, Action<Exception> exceptionAssertion, [CallerMemberName] string callerMemberName = "", [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = 0)
{
(this as IFixtureSteppable).Stepper.Take(new AssertStep(description, exceptionAssertion, GetType(), callerMemberName, callerFilePath, callerLineNumber));
}
IFixtureStepper IFixtureSteppable.Stepper { get; set; }
}
Finally, define runners that implement IFixtureStepRunner to run these steps.
public class ArrangeStepRunner : FixtureStepRunner<ArrangeStep>
{
public ArrangeStepRunner(ArrangeStep step) : base(step)
{
}
protected override FixtureStepResult.Builder Run(FixtureStepResultCollection results)
{
if (Step.Arrangement == null)
{
return FixtureStepResult.Of(Step).Pending();
}
try
{
Step.Arrangement?.Invoke();
return FixtureStepResult.Of(Step).Passed();
}
catch (Exception exc)
{
return FixtureStepResult.Of(Step).Failed(exc);
}
}
}
public class ActStepRunner : FixtureStepRunner<ActStep>
{
public ActStepRunner(ActStep step) : base(step)
{
}
protected override FixtureStepResult.Builder Run(FixtureStepResultCollection results)
{
if (Step.Action == null)
{
return FixtureStepResult.Of(Step).Pending();
}
try
{
Step.Action?.Invoke();
return FixtureStepResult.Of(Step).Passed();
}
catch (Exception exc)
{
return FixtureStepResult.Of(Step).Failed(exc);
}
}
}
public class AssertStepRunner : FixtureStepRunner<AssertStep>
{
public AssertStepRunner(AssertStep step) : base(step)
{
}
protected override FixtureStepResult.Builder Run(FixtureStepResultCollection results)
{
if (IsPending)
{
return FixtureStepResult.Of(Step).Pending();
}
try
{
Step.ExecuteAssertion(Step.Assertion);
Step.Action?.Invoke();
if (HasAssertionWithException)
{
var exception = results.GetLatestExceptionAt<ActStep>();
Step.ExecuteAssertion(Step.ExceptionAssertion, exception);
Step.ExceptionAction?.Invoke(exception);
results.ClearException(exception);
}
return FixtureStepResult.Of(Step).Passed();
}
catch (Exception exc)
{
return FixtureStepResult.Of(Step).Failed(exc);
}
}
private bool IsPending => !HasAssertionWithoutException && !HasAssertionWithException;
private bool HasAssertionWithoutException => Step.Assertion != null || Step.Action != null;
private bool HasAssertionWithException => Step.ExceptionAssertion != null || Step.ExceptionAction != null;
}
[Specification]
class LoginAuthenticationSpec : ArrangeActAssertSteppable
{
[Example("Invalid user name or password is specified")]
void Ex01()
{
Arrange("an invalid user name");
Arrange("an invalid password");
Act("the authentication");
Assert("that the user should not be authenticated");
}
}