Skip to content

Commit

Permalink
Merge pull request #3744 from gleb-osokin/master
Browse files Browse the repository at this point in the history
Apply FixtureLifeCycle to parametrized tests
  • Loading branch information
rprouse committed Jan 30, 2021
2 parents 6a7fc74 + c3ba3ae commit c7afecb
Show file tree
Hide file tree
Showing 7 changed files with 442 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace NUnit.Framework.Internal.Commands
/// <summary>
/// ConstructFixtureCommand constructs the user test object if necessary.
/// </summary>
public class FixturePerTestCaseCommand : BeforeAndAfterTestCommand
public class FixturePerTestCaseCommand : BeforeTestCommand
{
/// <summary>
/// Handles the construction and disposement of a fixture per test case
Expand All @@ -38,31 +38,27 @@ public class FixturePerTestCaseCommand : BeforeAndAfterTestCommand
public FixturePerTestCaseCommand(TestCommand innerCommand)
: base(innerCommand)
{
TestSuite testSuite = null;
TestFixture testFixture = null;

ITest currentTest = Test;
while (currentTest != null && testSuite == null)
while (currentTest != null && testFixture == null)
{
testSuite = testSuite ?? currentTest as TestSuite;
testFixture = currentTest as TestFixture;
currentTest = currentTest.Parent;
}

Guard.ArgumentValid(testSuite != null, "FixturePerTestCaseCommand must reference a TestSuite", nameof(innerCommand));
Guard.ArgumentValid(testFixture != null, "FixturePerTestCaseCommand must reference a TestFixture", nameof(innerCommand));

ITypeInfo typeInfo = testSuite.TypeInfo;
ITypeInfo typeInfo = testFixture.TypeInfo;

BeforeTest = (context) =>
{
if (typeInfo != null && !typeInfo.IsStaticClass)
{
context.TestObject = typeInfo.Construct(testSuite.Arguments);
context.TestObject = typeInfo.Construct(testFixture.Arguments);
Test.Fixture = context.TestObject;
}
};

AfterTest = (context) =>
{
};
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
using NUnit.Framework.Internal.Commands;
using NUnit.Framework.Interfaces;
using System.Diagnostics;
using NUnit.Framework.Internal.Extensions;

namespace NUnit.Framework.Internal.Execution
{
Expand Down Expand Up @@ -162,7 +163,7 @@ private bool CheckForCancellation()

private void InitializeSetUpAndTearDownCommands()
{
var methodValidator = CheckInstancePerTestCase()
var methodValidator = Test.HasLifeCycle(LifeCycle.InstancePerTestCase)
? new StaticMethodValidator(
$"Only static OneTimeSetUp and OneTimeTearDown are allowed for {nameof(LifeCycle.InstancePerTestCase)} mode.")
: null;
Expand Down Expand Up @@ -203,19 +204,6 @@ private void InitializeSetUpAndTearDownCommands()
_teardownCommand = MakeOneTimeTearDownCommand(setUpTearDownItems, actionItems);
}

private bool CheckInstancePerTestCase()
{
ITest test = Test;
while (test != null)
{
if (test is TestFixture fixture && fixture.LifeCycle == LifeCycle.InstancePerTestCase)
return true;

test = test.Parent;
}
return false;
}

private TestCommand MakeOneTimeSetUpCommand(List<SetUpTearDownItem> setUpTearDown, List<TestActionItem> actions)
{
TestCommand command = new EmptyTestCommand(Test);
Expand Down Expand Up @@ -261,7 +249,7 @@ private TestCommand MakeOneTimeTearDownCommand(List<SetUpTearDownItem> setUpTear
command = new OneTimeTearDownCommand(command, item);

// Dispose of fixture if necessary
if (Test is IDisposableFixture && typeof(IDisposable).IsAssignableFrom(Test.TypeInfo.Type) && !CheckInstancePerTestCase())
if (Test is IDisposableFixture && typeof(IDisposable).IsAssignableFrom(Test.TypeInfo.Type) && !Test.HasLifeCycle(LifeCycle.InstancePerTestCase))
command = new DisposeFixtureCommand(command);

return command;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using NUnit.Framework.Interfaces;
using NUnit.Framework.Internal.Abstractions;
using NUnit.Framework.Internal.Commands;
using NUnit.Framework.Internal.Extensions;

namespace NUnit.Framework.Internal.Execution
{
Expand Down Expand Up @@ -138,7 +139,7 @@ private TestCommand MakeTestCommand()
command = new SetUpTearDownCommand(command, item);

// Dispose of fixture if necessary
var isInstancePerTestCase = parentFixture?.LifeCycle == LifeCycle.InstancePerTestCase;
var isInstancePerTestCase = Test.HasLifeCycle(LifeCycle.InstancePerTestCase);
if (isInstancePerTestCase && parentFixture is IDisposableFixture && typeof(IDisposable).IsAssignableFrom(parentFixture.TypeInfo.Type))
command = new DisposeFixtureCommand(command);

Expand Down
42 changes: 42 additions & 0 deletions src/NUnitFramework/framework/Internal/Extensions/TestExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// ***********************************************************************
// Copyright (c) 2021 Charlie Poole, Rob Prouse
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ***********************************************************************

using NUnit.Framework.Interfaces;

namespace NUnit.Framework.Internal.Extensions
{
internal static class TestExtensions
{
public static bool HasLifeCycle(this ITest test, LifeCycle lifeCycle)
{
while (test != null)
{
if (test is TestFixture fixture && fixture.LifeCycle == LifeCycle.InstancePerTestCase)
return lifeCycle == LifeCycle.InstancePerTestCase;

test = test.Parent;
}
return lifeCycle != LifeCycle.InstancePerTestCase;
}
}
}
179 changes: 179 additions & 0 deletions src/NUnitFramework/testdata/AssemblyLevelLifeCycleFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// ***********************************************************************
// Copyright (c) 2021 Charlie Poole, Rob Prouse
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ***********************************************************************

namespace NUnit.TestData.LifeCycleTests
{
public static class AssemblyLevelFixtureLifeCycleTest
{
public const string Code = @"
using NUnit.Framework;
[assembly: FixtureLifeCycle(LifeCycle.InstancePerTestCase)]
[TestFixture]
public class FixtureUnderTest
{
private int _value;
[Test]
public void Test1()
{
Assert.AreEqual(0, _value++);
}
[Test]
public void Test2()
{
Assert.AreEqual(0, _value++);
}
}
";
}

public static class AssemblyLevelLifeCycleTestFixtureSourceTest
{
public const string Code = @"
using NUnit.Framework;
[assembly: FixtureLifeCycle(LifeCycle.InstancePerTestCase)]
[TestFixtureSource(""FixtureArgs"")]
public class FixtureUnderTest
{
private readonly int _initialValue;
private int _value;
public FixtureUnderTest(int num)
{
_initialValue = num;
_value = num;
}
public static int[] FixtureArgs()
{
return new int[] { 1, 42 };
}
[Test]
public void Test1()
{
Assert.AreEqual(_initialValue, _value++);
}
[Test]
public void Test2()
{
Assert.AreEqual(_initialValue, _value++);
}
}
";
}

public static class AssemblyLevelLifeCycleFixtureWithTestCasesTest
{
public const string Code = @"
using NUnit.Framework;
[assembly: FixtureLifeCycle(LifeCycle.InstancePerTestCase)]
public class FixtureUnderTest
{
private int _counter;
[TestCase(0)]
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
public void Test(int _)
{
Assert.AreEqual(0, _counter++);
}
}
";
}

public static class AssemblyLevelLifeCycleFixtureWithTestCaseSourceTest
{
public const string Code = @"
using NUnit.Framework;
[assembly: FixtureLifeCycle(LifeCycle.InstancePerTestCase)]
public class FixtureUnderTest
{
private int _counter;
public static int[] Args()
{
return new int[] { 1, 42 };
}
[TestCaseSource(""Args"")]
public void Test(int _)
{
Assert.AreEqual(0, _counter++);
}
}
";
}

public static class AssemblyLevelLifeCycleFixtureWithValuesTest
{
public const string Code = @"
using NUnit.Framework;
[assembly: FixtureLifeCycle(LifeCycle.InstancePerTestCase)]
public class FixtureUnderTest
{
private int _counter;
[Test]
public void Test([Values] bool? _)
{
Assert.AreEqual(0, _counter++);
}
}
";
}

public static class AssemblyLevelLifeCycleFixtureWithTheoryTest
{
public const string Code = @"
using NUnit.Framework;
[assembly: FixtureLifeCycle(LifeCycle.InstancePerTestCase)]
public class FixtureUnderTest
{
private int _counter;
[Theory]
public void Test(bool? _)
{
Assert.AreEqual(0, _counter++);
}
}
";
}
}

0 comments on commit c7afecb

Please sign in to comment.