I have a set of unit tests that require TestInitialize to run for them to work... however, there is one specific test that i'd love to be able to run without running TestInitialize. Is there a way to do that?
It might look like this:
[TestClass]
public class BingBangBoom
{
[TestInitialize]
public void Setup()
{
// ...
}
[TestMethod]
public void Bing()
{
// ...
}
[TestMethod]
public void Bang()
{
// ...
}
[TestMethod(PreventInitialize)]
public void Boom
{
// ...
}
}
No worries if not, I can come up with an alternative solution
Edit - RE DavidG:
It seems a shame to have this:
[TestClass]
public class BingBangBoom
{
[TestInitialize]
public void Setup()
{
// ...
}
// 10 very related methods
}
[TestClass]
public class BingBangBoom2
{
// 1 method, even though it's entirely related to BingBangBoomin'
}
I guess it is what it is.
That's not immediately obvious, but surely doable.
Assuming you have attribute like this:
public class SkipInitializeAttribute : Attribute { }
The thing you need is public property inside your test class to be injected by testing framework:
public TestContext TestContext { get; set; }
And then just branch your initialization like this:
[TestInitialize]
public void Initialize()
{
bool skipInitialize = GetType().GetMethod(TestContext.TestName)
.GetCustomAttributes<SkipInitializeAttribute>().Any();
if (!skipInitialize)
{
// Initialization code here
}
}
Working sample as self-tested solution:
using System;
using System.Linq;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTestProject1
{
public class SkipInitializeAttribute : Attribute
{
}
[TestClass]
public class UnitTest1
{
public TestContext TestContext { get; set; }
private bool IsInitializationDone { get; set; }
[TestInitialize]
public void Initialize()
{
bool skipInitialize = GetType().GetMethod(TestContext.TestName).GetCustomAttributes<SkipInitializeAttribute>().Any();
if (!skipInitialize)
{
// Initialization code here
IsInitializationDone = true;
}
}
[TestMethod]
public void TestMethod1()
{
Assert.IsTrue(IsInitializationDone);
}
[TestMethod]
[SkipInitialize]
public void TestMethod2()
{
Assert.IsFalse(IsInitializationDone);
}
[TestMethod]
public void TestMethod3()
{
Assert.IsTrue(IsInitializationDone);
}
}
}
And results:
Starting test execution, please wait...
Passed TestMethod1
Passed TestMethod2
Passed TestMethod3
Total tests: 3. Passed: 3. Failed: 0. Skipped: 0.
Test Run Successful.
Having this general idea in mind you can play with base class / helpers etc.
Related
I have these two tests (stripped to the bare bones to replicate the error):
[TestFixture]
public class CreditorMapperTests
{
private IAbcContext _AbcContext;
[SetUp]
public void Setup()
{
_AbcContext = Substitute.For<IAbcContext>();
_AbcContext.CompanyInfo.Returns(x => new CompanyInfo(Arg.Any<Guid>()));
}
[Test]
public void A()
{
Creditor publishDocument = new Creditor();
publishDocument.CompanyExternalId = _AbcContext.CompanyInfo.UniqueId;
}
[Test]
public void B()
{
Creditor publishDocument = new Creditor();
publishDocument.CompanyExternalId = _AbcContext.CompanyInfo.UniqueId;
}
}
public interface IAbcContext
{
CompanyInfo CompanyInfo { get; }
}
public class CompanyInfo
{
public CompanyInfo(Guid uniqueId)
{
UniqueId = uniqueId;
}
public readonly Guid UniqueId;
}
public class Creditor
{
public Guid CompanyExternalId { get; set; }
}
The Setup() for A() runs fine. However when Setup() is called for B(), I get this error:
NSubstitute.Exceptions.UnexpectedArgumentMatcherException : Argument
matchers (Arg.Is, Arg.Any) should only be used in place of member
arguments. Do not use in a Returns() statement or anywhere else
outside of a member call. Correct use:
sub.MyMethod(Arg.Any()).Returns("hi") Incorrect use:
sub.MyMethod("hi").Returns(Arg.Any())
This only happens when I run both tests by running all tests in that class.
If I run B() by itself, the Exception is not thrown.
Why does Setup() for B() fail only when run automatically after A()?
(nb. both tests are identical).
I'm using NUnit v3.8.1 and NSubstitute v2.0.3
I have some test methods which are spread across multiple test classes but belonging to single test collection. I am using ITestCaseOrderer provided by xUnit but it is ordering only test methods within individual test classes.
[AttributeUsage(AttributeTargets.Method)]
public class TestPriorityAttribute : Attribute
{
public TestPriorityAttribute(int priority)
{
this.Priority = priority;
}
public int Priority { get; }
}
I have implemented my priority orderer in the below fashion.
public class PriorityOrderer : ITestCaseOrderer
{
public IEnumerable<TTestCase> OrderTestCases<TTestCase>(IEnumerable<TTestCase> testCases) where TTestCase : ITestCase
{
var sortedMethods = new Dictionary<int, TTestCase>();
foreach (var testCase in testCases)
{
var attributeInfo = testCase.TestMethod.Method.GetCustomAttributes(typeof(TestPriorityAttribute).AssemblyQualifiedName)
.SingleOrDefault();
if (attributeInfo != null)
{
var priority = attributeInfo.GetNamedArgument<int>("Priority");
sortedMethods.Add(priority, testCase);
}
}
return sortedMethods.OrderBy(x => x.Key).Select(x => x.Value);
}
}
My First test class looks like this.
[TestCaseOrderer("Integration.Tests.PriorityOrderer", "CompanyName.ProjectName.Integration.Tests")]
[Collection("StandardIntegrationTests")]
[Trait("Category", "Integration")]
public class StandardControllerTests1
{
public StandardControllerTests1(StandardIntegrationTestFixture standardIntegrationTestFixture)
{
}
[Fact, TestPriority(1)]
public void TestMethod1()
{
}
[Fact, TestPriority(2)]
public void TestMethod2()
{
}
}
My Second test class looks like this
[TestCaseOrderer("Integration.Tests.PriorityOrderer", "CompanyName.ProjectName.Integration.Tests")]
[Collection("StandardIntegrationTests")]
[Trait("Category", "Integration")]
public class StandardControllerTests2
{
public StandardControllerTests2(StandardIntegrationTestFixture standardIntegrationTestFixture)
{
}
[Fact, TestPriority(3)]
public void TestMethod3()
{
}
[Fact, TestPriority(4)]
public void TestMethod4()
{
}
}
I have other test classes also which belong to same test collection. When I run the tests, It is not ordering across the collection. How do I order these tests to run in order which are in same collection?
I have run into this same issue while building a ordering system to run tests in parallel with xUnit while also running some with dependencies within their own collections.
According to a Issue 898 on the xUnit board ordering across classes in the same collection is not possible at this time.
The work around I used was to organize the tests which needed sequenced into the same class with different files using partial class so that the tests could still be kept organized into different files.
[Trait("Order", "")]
[TestCaseOrderer(DependencyOrderer.TypeName, DependencyOrderer.AssemblyName)]
public partial class OrderTests
{
[Fact]
public void Test0()
{
Thread.Sleep(TimeSpan.FromSeconds(1));
}
[Fact]
[TestDependency("Test1", "Test0")]
public void Test3()
{
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}
[Trait("Order", "")]
public partial class OrderTests
{
[Fact]
public void Test2()
{
Thread.Sleep(TimeSpan.FromSeconds(1));
}
[Fact]
[TestDependency("Test0")]
public void Test1()
{
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}
Using C# 4.0. I have a unit test where I need to create a temporary database. This is done during class initialization:
[ClassInitialize()]
public static void MyClassInitialize(TestContext testContext)
{
// Create database!
}
[ClassCleanup()]
public static void MyClassCleanup()
{
// Remove database IF CurrentTestOutcome == UnitTestOutcome.Passed
}
Q: How can I read the CurrentTestOutcome value from my ClassCleanup method?
I'd suggest that you create a static flag that you can access from your MyClassCleanup and set it during test cleanup when you get an error. Something like the following on your class:
[TestClass]
public class MyTests {
static bool _testFailed;
[ClassInitialize()]
public static void MyClassInitialize(TestContext testContext)
{
// Create database!
_testFailed = false;
}
[ClassCleanup()]
public static void MyClassCleanup()
{
if(_testFailed == false) {
// Remove database IF CurrentTestOutcome == UnitTestOutcome.Passed
}
}
[TestCleanup()]
public void MyTestCleanup() {
if (TestContext.CurrentTestOutcome != UnitTestOutcome.Passed) {
_testFailed = true;
}
}
public TestContext TestContext { get; set; }
}
I'm suggesting the above approach because my understanding is that in your class cleanup, referring to CurrentTestOutcome wouldn't really make much since. It would simply contain the state of the last test to run, not the combined outcome from all of the tests in the class.
I'm considering the following solution. Not sure if this may be considered good practice or not.
private static IList<TestContext> testResults;
public TestContext TestContext
{
get
{
return testContext;
}
set
{
testContext = value;
testResults.Add(testContext);
}
}
[ClassInitialize()]
public static void MyClassInitialize(TestContext testContext)
{
testResults = new List<TestContext>();
}
[ClassCleanup()]
public static void MyClassCleanup()
{
if (testResults.All(t => t.CurrentTestOutcome == UnitTestOutcome.Passed ||
t.CurrentTestOutcome == UnitTestOutcome.Inconclusive))
{
// Perform conditional cleanup!
}
}
Is there a way to add arguments to an nunit setup method like this: public void SetUp(Point p = null) { /*code*/ }.
I tried it and got the following exception SetUp : System.Reflection.TargetParameterCountException : Parameter count mismatch
I think that your point is to avoid code duplication.
Try to extract base class with overriten method used in SetUp().
All derived class will execute tests from base class, with objects prepared in overriten OnSetUp()
[TestFixture]
public class BaseTestsClass
{
//some public/protected fields to be set in SetUp and OnSetUp
[SetUp]
public void SetUp()
{
//basic SetUp method
OnSetUp();
}
public virtual void OnSetUp()
{
}
[Test]
public void SomeTestCase()
{
//...
}
[Test]
public void SomeOtherTestCase()
{
//...
}
}
[TestFixture]
public class TestClassWithSpecificSetUp : BaseTestsClass
{
public virtual void OnSetUp()
{
//setup some fields
}
}
[TestFixture]
public class OtherTestClassWithSpecificSetUp : BaseTestsClass
{
public virtual void OnSetUp()
{
//setup some fields
}
}
Using parametrised TestFixture also can be usefull. Tests in class will be lunched per TestFixture, SetUp method also.
But remember that
Parameterized fixtures are (as you have discovered) limited by the fact that you can only use arguments that are permitted in attributes
Usage:
[TestFixture("some param", 123)]
[TestFixture("another param", 456)]
public class SomeTestsClass
{
private readonly string _firstParam;
private readonly int _secondParam;
public WhenNoFunctionCodeExpected(string firstParam, int secondParam)
{
_firstParam = firstParam;
_secondParam = secondParam;
}
[Test]
public void SomeTestCase()
{
...
}
}
I keep the test data for specific test method in folder named the same as function. I previously had the same function call in each [TestMethod], ClearAllAndLoadTestMethodData() which determined the method name via StackTrace. Now, I moved this function to [TestInitialize]. How can I find the name of the method that is about to be executed?
I thought TestContext provide this. I have access to it via [AssemblyInitialize()] and on first run its property Name is set to name of the testmethod. However, later this doesn't change (if I save the object in static field).
The AssemblyInitialize method is executed only once before all your tests.
Use the TestContext inside the TestInitialize method:
[TestClass]
public class TestClass
{
[TestInitialize]
public void TestIntialize()
{
string testMethodName = TestContext.TestName;
}
[TestMethod]
public void TestMethod()
{
}
public TestContext TestContext { get; set; }
}
[TestClass]
public class MyTestClass
{
private static TestContext _testContext;
[ClassInitialize]
public static void TestFixtureSetup(TestContext context)
{
_testContext = context;
}
[TestInitialize]
public void TestIntialize()
{
string testMethodName = MyTestClass._testContext.TestName;
switch (testMethodName)
{
case "TestMethodA":
//todo..
break;
case "TestMethodB":
//todo..
break;
default:
break;
}
}
[TestMethod]
public void TestMethodA()
{
}
[TestMethod]
public void TestMethodB()
{
}
}