Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Skip cases #24

Merged
merged 3 commits into from

2 participants

@mexx

fixes #17

@plioi
Owner

This is fantastic. I appreciate the attention to detail, especially the realization that when every test is skipped in a class, we shouldn't bother constructing the class.

@plioi plioi merged commit c46730f into from
@mexx mexx deleted the branch
@mexx

I'm pleased that you like it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 28, 2013
  1. @mexx

    add ability to skip tests

    mexx authored
  2. @mexx
  3. @mexx

    report skipped case count

    mexx authored
This page is out of date. Refresh to see the latest.
View
4 src/Fixie.Samples/Fixie.Samples.csproj
@@ -64,6 +64,10 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Shuffle\CustomConvention.cs" />
<Compile Include="Shuffle\OrderTests.cs" />
+ <Compile Include="Skipped\SkipAttribute.cs" />
+ <Compile Include="Skipped\SkipClassTests.cs" />
+ <Compile Include="Skipped\CustomConvention.cs" />
+ <Compile Include="Skipped\SkipMethodTests.cs" />
<Compile Include="StringBuilderExtensions.cs" />
<Compile Include="xUnitStyle\CalculatorTests.cs" />
<Compile Include="xUnitStyle\CustomConvention.cs" />
View
25 src/Fixie.Samples/Skipped/CustomConvention.cs
@@ -0,0 +1,25 @@
+using System;
+using Fixie.Conventions;
+
+namespace Fixie.Samples.Skipped
+{
+ public class CustomConvention : Convention
+ {
+ public CustomConvention()
+ {
+ Classes
+ .Where(type => type.IsInNamespace(GetType().Namespace))
+ .NameEndsWith("Tests");
+
+ Methods
+ .Where(method => method.IsVoid());
+
+ CaseExecution
+ .Skip(@case => @case.Method.HasOrInherits<SkipAttribute>() || @case.Method.DeclaringType.HasOrInherits<SkipAttribute>());
+
+ ClassExecution
+ .CreateInstancePerTestClass()
+ .SortCases((caseA, caseB) => String.Compare(caseA.Name, caseB.Name, StringComparison.Ordinal));
+ }
+ }
+}
View
7 src/Fixie.Samples/Skipped/SkipAttribute.cs
@@ -0,0 +1,7 @@
+using System;
+
+namespace Fixie.Samples.Skipped
+{
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
+ public class SkipAttribute : Attribute { }
+}
View
13 src/Fixie.Samples/Skipped/SkipClassTests.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Fixie.Samples.Skipped
+{
+ [Skip]
+ public class SkipClassTests
+ {
+ public void ShouldNotBeCalled()
+ {
+ throw new Exception("This test should be skipped.");
+ }
+ }
+}
View
47 src/Fixie.Samples/Skipped/SkipMethodTests.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Text;
+using Should;
+
+namespace Fixie.Samples.Skipped
+{
+ public class SkipMethodTests : IDisposable
+ {
+ Calculator calculator;
+ readonly StringBuilder log;
+
+ public SkipMethodTests()
+ {
+ calculator = new Calculator();
+ log = new StringBuilder();
+ log.WhereAmI();
+ }
+
+ public void ShouldAdd()
+ {
+ log.WhereAmI();
+ calculator.Add(2, 3).ShouldEqual(5);
+ }
+
+ [Skip]
+ public void ShouldNotBeCalled()
+ {
+ throw new Exception("This test should be skipped.");
+ }
+
+ public void ShouldSubtract()
+ {
+ log.WhereAmI();
+ calculator.Subtract(5, 3).ShouldEqual(2);
+ }
+
+ public void Dispose()
+ {
+ log.WhereAmI();
+ log.ShouldHaveLines(
+ ".ctor",
+ "ShouldAdd",
+ "ShouldSubtract",
+ "Dispose");
+ }
+ }
+}
View
9 src/Fixie.TestDriven/TestDrivenListener.cs
@@ -16,6 +16,15 @@ public void AssemblyStarted(Assembly assembly)
{
}
+ public void CaseSkipped(Case @case)
+ {
+ tdnet.TestFinished(new TestResult
+ {
+ Name = @case.Name,
+ State = TestState.Ignored
+ });
+ }
+
public void CasePassed(PassResult result)
{
var @case = result.Case;
View
15 src/Fixie.Tests/Conventions/ConventionTests.cs
@@ -161,12 +161,13 @@ public void ShouldExecuteAllCasesInAllDiscoveredTestClasses()
var listener = new StubListener();
var convention = new SelfTestConvention();
- convention.Execute(listener, typeof(SampleIrrelevantClass), typeof(PassTestClass), typeof(int), typeof(PassFailTestClass));
+ convention.Execute(listener, typeof(SampleIrrelevantClass), typeof(PassTestClass), typeof(int), typeof(PassFailTestClass), typeof(SkipTestClass));
listener.Entries.ShouldEqual("Fixie.Tests.Conventions.ConventionTests+PassTestClass.PassA passed.",
"Fixie.Tests.Conventions.ConventionTests+PassTestClass.PassB passed.",
"Fixie.Tests.Conventions.ConventionTests+PassFailTestClass.Fail failed: 'Fail' failed!",
- "Fixie.Tests.Conventions.ConventionTests+PassFailTestClass.Pass passed.");
+ "Fixie.Tests.Conventions.ConventionTests+PassFailTestClass.Pass passed.",
+ "Fixie.Tests.Conventions.ConventionTests+SkipTestClass.Skip skipped.");
}
public void ShouldAllowRandomShufflingOfCaseExecutionOrder()
@@ -178,12 +179,13 @@ public void ShouldAllowRandomShufflingOfCaseExecutionOrder()
.CreateInstancePerTestClass()
.ShuffleCases(new Random(1));
- convention.Execute(listener, typeof(SampleIrrelevantClass), typeof(PassTestClass), typeof(int), typeof(PassFailTestClass));
+ convention.Execute(listener, typeof(SampleIrrelevantClass), typeof(PassTestClass), typeof(int), typeof(PassFailTestClass), typeof(SkipTestClass));
listener.Entries.ShouldEqual("Fixie.Tests.Conventions.ConventionTests+PassTestClass.PassB passed.",
"Fixie.Tests.Conventions.ConventionTests+PassTestClass.PassA passed.",
"Fixie.Tests.Conventions.ConventionTests+PassFailTestClass.Fail failed: 'Fail' failed!",
- "Fixie.Tests.Conventions.ConventionTests+PassFailTestClass.Pass passed.");
+ "Fixie.Tests.Conventions.ConventionTests+PassFailTestClass.Pass passed.",
+ "Fixie.Tests.Conventions.ConventionTests+SkipTestClass.Skip skipped.");
}
class SampleIrrelevantClass
@@ -203,5 +205,10 @@ class PassFailTestClass
public void Pass() { }
public void Fail() { throw new FailureException(); }
}
+
+ class SkipTestClass
+ {
+ public void Skip() { throw new ShouldBeUnreachableException(); }
+ }
}
}
View
68 src/Fixie.Tests/Lifecycle/ConstructionTests.cs
@@ -150,6 +150,74 @@ public void ShouldFailAllCasesWhenConstructingPerTestClassAndCustomFactoryThrows
"Factory");
}
+ public void ShouldSkipConstructingPerCaseWhenAllCasesSkipped()
+ {
+ Convention.ClassExecution
+ .CreateInstancePerCase();
+
+ Convention.CaseExecution
+ .Skip(x => true);
+
+ var output = Run();
+
+ output.ShouldHaveResults(
+ "SampleTestClass.Pass skipped.",
+ "SampleTestClass.Fail skipped.");
+
+ output.ShouldHaveLifecycle();
+ }
+
+ public void ShouldSkipConstructingPerTestClassWhenAllCasesSkipped()
+ {
+ Convention.ClassExecution
+ .CreateInstancePerTestClass();
+
+ Convention.CaseExecution
+ .Skip(x => true);
+
+ var output = Run();
+
+ output.ShouldHaveResults(
+ "SampleTestClass.Pass skipped.",
+ "SampleTestClass.Fail skipped.");
+
+ output.ShouldHaveLifecycle();
+ }
+
+ public void ShouldSkipConstructingPerCaseUsingCustomFactoryWhenAllCasesSkipped()
+ {
+ Convention.ClassExecution
+ .CreateInstancePerCase(Factory);
+
+ Convention.CaseExecution
+ .Skip(x => true);
+
+ var output = Run();
+
+ output.ShouldHaveResults(
+ "SampleTestClass.Pass skipped.",
+ "SampleTestClass.Fail skipped.");
+
+ output.ShouldHaveLifecycle();
+ }
+
+ public void ShouldSkipConstructingPerTestClassUsingCustomFactoryWhenAllCasesSkipped()
+ {
+ Convention.ClassExecution
+ .CreateInstancePerTestClass(Factory);
+
+ Convention.CaseExecution
+ .Skip(x => true);
+
+ var output = Run();
+
+ output.ShouldHaveResults(
+ "SampleTestClass.Pass skipped.",
+ "SampleTestClass.Fail skipped.");
+
+ output.ShouldHaveLifecycle();
+ }
+
static object Factory(Type testClass)
{
WhereAmI();
View
3  src/Fixie.Tests/Listeners/ConsoleListenerTests.cs
@@ -22,6 +22,7 @@ public void ShouldReportResultsToTheConsole()
console.Lines()
.Select(x => Regex.Replace(x, @":line \d+", ":line #")) //Avoid brittle assertion introduced by stack trace line numbers.
.ShouldEqual(
+ "Test '" + testClass + ".SkipA' skipped",
"Console.Out: FailA",
"Console.Error: FailA",
"Console.Out: FailB",
@@ -70,6 +71,8 @@ public void FailB()
public void PassC() { WhereAmI(); }
+ public void SkipA() { throw new ShouldBeUnreachableException(); }
+
static void WhereAmI([CallerMemberName] string member = null)
{
Console.Out.WriteLine("Console.Out: " + member);
View
4 src/Fixie.Tests/Listeners/TeamCityListenerTests.cs
@@ -23,6 +23,8 @@ public void ShouldReportResultsToTheConsoleInTeamCityFormat()
.Select(x => Regex.Replace(x, @":line \d+", ":line #")) //Avoid brittle assertion introduced by stack trace line numbers.
.Select(x => Regex.Replace(x, @"duration='\d+'", "duration='#'")) //Avoid brittle assertion introduced by durations.
.ShouldEqual(
+ "##teamcity[testIgnored name='" + testClass + ".SkipA']",
+
"Console.Out: FailA",
"Console.Error: FailA",
"Console.Out: FailB",
@@ -83,6 +85,8 @@ public void FailB()
public void PassC() { WhereAmI(); }
+ public void SkipA() { throw new ShouldBeUnreachableException(); }
+
static void WhereAmI([CallerMemberName] string member = null)
{
Console.Out.WriteLine("Console.Out: " + member);
View
5 src/Fixie.Tests/StubListener.cs
@@ -14,6 +14,11 @@ public void AssemblyStarted(Assembly assembly)
{
}
+ public void CaseSkipped(Case @case)
+ {
+ log.Add(string.Format("{0} skipped.", @case.Name));
+ }
+
public void CasePassed(PassResult result)
{
var @case = result.Case;
View
8 src/Fixie/AssemblyResult.cs
@@ -7,20 +7,24 @@ public class AssemblyResult
{
readonly int passed;
readonly int failed;
+ readonly int skipped;
- public AssemblyResult(int passed, int failed)
+ public AssemblyResult(int passed, int skipped, int failed)
{
this.passed = passed;
this.failed = failed;
+ this.skipped = skipped;
}
public int Passed { get { return passed; } }
+ public int Skipped { get { return skipped; } }
+
public int Failed { get { return failed; } }
public int Total
{
- get { return Passed + Failed; }
+ get { return Passed + Skipped + Failed; }
}
}
}
View
9 src/Fixie/Conventions/CaseBehaviorBuilder.cs
@@ -11,10 +11,13 @@ public class CaseBehaviorBuilder
public CaseBehaviorBuilder()
{
Behavior = new Invoke();
+ SkipPredicate = @case => false;
}
public CaseBehavior Behavior { get; private set; }
+ public Func<Case, bool> SkipPredicate { get; private set; }
+
public CaseBehaviorBuilder Wrap(CaseBehaviorAction outer)
{
Behavior = new WrapBehavior(outer, Behavior);
@@ -31,6 +34,12 @@ public CaseBehaviorBuilder SetUpTearDown(CaseAction setUp, CaseAction tearDown)
});
}
+ public CaseBehaviorBuilder Skip(Func<Case, bool> skipPredicate)
+ {
+ SkipPredicate = skipPredicate;
+ return this;
+ }
+
class WrapBehavior : CaseBehavior
{
readonly CaseBehaviorAction outer;
View
11 src/Fixie/Conventions/Convention.cs
@@ -38,8 +38,17 @@ public void Execute(Listener listener, params Type[] candidateTypes)
var methods = Methods.Filter(testClass);
var cases = methods.SelectMany(method => CasesForMethod(testClass, method)).ToArray();
+ var casesBySkipState = cases.ToLookup(CaseExecution.SkipPredicate);
+ var casesToSkip = casesBySkipState[true];
+ var casesToExecute = casesBySkipState[false];
+ foreach (var @case in casesToSkip)
+ {
+ listener.CaseSkipped(@case);
+ }
- var caseExecutions = cases.Select(@case => new CaseExecution(@case)).ToArray();
+ var caseExecutions = casesToExecute.Select(@case => new CaseExecution(@case)).ToArray();
+ if (!caseExecutions.Any())
+ continue;
ClassExecution.Behavior.Execute(testClass, this, caseExecutions);
View
3  src/Fixie/Conventions/SelfTestConvention.cs
@@ -15,6 +15,9 @@ public SelfTestConvention()
ClassExecution
.SortCases((x, y) => String.Compare(x.Name, y.Name, StringComparison.Ordinal));
+
+ CaseExecution
+ .Skip(@case => @case.Method.Name.StartsWith("Skip"));
}
}
}
View
5 src/Fixie/Foreground.cs
@@ -22,6 +22,11 @@ public static Foreground Red
get { return new Foreground(ConsoleColor.Red); }
}
+ public static Foreground Yellow
+ {
+ get { return new Foreground(ConsoleColor.Yellow); }
+ }
+
public static Foreground DarkGray
{
get { return new Foreground(ConsoleColor.DarkGray); }
View
1  src/Fixie/Listener.cs
@@ -5,6 +5,7 @@ namespace Fixie
public interface Listener
{
void AssemblyStarted(Assembly assembly);
+ void CaseSkipped(Case @case);
void CasePassed(PassResult result);
void CaseFailed(FailResult result);
void AssemblyCompleted(Assembly assembly, AssemblyResult result);
View
8 src/Fixie/Listeners/ConsoleListener.cs
@@ -11,6 +11,12 @@ public void AssemblyStarted(Assembly assembly)
Console.WriteLine();
}
+ public void CaseSkipped(Case @case)
+ {
+ using (Foreground.Yellow)
+ Console.WriteLine("Test '{0}' skipped", @case.Name);
+ }
+
public void CasePassed(PassResult result)
{
}
@@ -32,7 +38,7 @@ public void AssemblyCompleted(Assembly assembly, AssemblyResult result)
var name = assemblyName.Name;
var version = assemblyName.Version;
- Console.WriteLine("{0} passed, {1} failed ({2} {3}).", result.Passed, result.Failed, name, version);
+ Console.WriteLine("{0} passed, {1} skipped, {2} failed ({3} {4}).", result.Passed, result.Skipped, result.Failed, name, version);
Console.WriteLine();
}
}
View
5 src/Fixie/Listeners/TeamCityListener.cs
@@ -12,6 +12,11 @@ public void AssemblyStarted(Assembly assembly)
Message("testSuiteStarted name='{0}'", assembly.FileName());
}
+ public void CaseSkipped(Case @case)
+ {
+ Message("testIgnored name='{0}'", @case.Name);
+ }
+
public void CasePassed(PassResult result)
{
var @case = result.Case;
View
10 src/Fixie/Runner.cs
@@ -122,6 +122,7 @@ class AssemblyResultListener : Listener
{
int passed;
int failed;
+ int skipped;
readonly Listener inner;
public AssemblyResultListener(Listener inner)
@@ -133,9 +134,16 @@ public void AssemblyStarted(Assembly assembly)
{
passed = 0;
failed = 0;
+ skipped = 0;
inner.AssemblyStarted(assembly);
}
+ public void CaseSkipped(Case @case)
+ {
+ skipped++;
+ inner.CaseSkipped(@case);
+ }
+
public void CasePassed(PassResult result)
{
passed++;
@@ -155,7 +163,7 @@ public void AssemblyCompleted(Assembly assembly, AssemblyResult result)
public AssemblyResult AssemblyResult
{
- get { return new AssemblyResult(passed, failed); }
+ get { return new AssemblyResult(passed, skipped, failed); }
}
}
}
Something went wrong with that request. Please try again.