I'm trying to run some setup code in one of my xUnit.net test classes, but although the tests are running it doesn't appear the constructor is.
Here's some of my code:
public abstract class LeaseTests<T>
{
private static readonly object s_lock = new object();
private static IEnumerable<T> s_sampleValues = Array.Empty<T>();
private static void AssignToSampleValues(Func<IEnumerable<T>, IEnumerable<T>> func)
{
lock (s_lock)
{
s_sampleValues = func(s_sampleValues);
}
}
public LeaseTests()
{
AssignToSampleValues(s => s.Concat(CreateSampleValues()));
}
public static IEnumerable<object[]> SampleValues()
{
foreach (T value in s_sampleValues)
{
yield return new object[] { value };
}
}
protected abstract IEnumerable<T> CreateSampleValues();
}
// Specialize the test class for different types
public class IntLeaseTests : LeaseTests<int>
{
protected override IEnumerable<int> CreateSampleValues()
{
yield return 3;
yield return 0;
yield return int.MaxValue;
yield return int.MinValue;
}
}
I'm using the SampleValues
as a MemberData
, so I can use them in tests like this
[Theory]
[MemberData(nameof(SampleValues))]
public void ItemShouldBeSameAsPassedInFromConstructor(T value)
{
var lease = CreateLease(value);
Assert.Equal(value, lease.Item);
}
However, I'm consistently getting an error saying that "no data was found for [method]", for all the methods that use SampleValues
. After I investigated further, I found out the LeaseTests
constructor wasn't even being run; when I set a breakpoint on the call to AssignToSampleValues
, it wasn't hit.
Why is this happening, and what can I do to fix it? Thanks.
The constructor is not running because MemberData
is evaluated before creating instance of specific test class. I'm not sure if this will fulfill your requirements, but you can do the following:
Define ISampleDataProvider
interface
public interface ISampleDataProvider<T>
{
IEnumerable<int> CreateSampleValues();
}
Add type specific implementation:
public class IntSampleDataProvider : ISampleDataProvider<int>
{
public IEnumerable<int> CreateSampleValues()
{
yield return 3;
yield return 0;
yield return int.MaxValue;
yield return int.MinValue;
}
}
Resolve and use data provider in SampleValues
method
public abstract class LeaseTests<T>
{
public static IEnumerable<object[]> SampleValues()
{
var targetType = typeof (ISampleDataProvider<>).MakeGenericType(typeof (T));
var sampleDataProviderType = Assembly.GetAssembly(typeof (ISampleDataProvider<>))
.GetTypes()
.FirstOrDefault(t => t.IsClass && targetType.IsAssignableFrom(t));
if (sampleDataProviderType == null)
{
throw new NotSupportedException();
}
var sampleDataProvider = (ISampleDataProvider<T>)Activator.CreateInstance(sampleDataProviderType);
return sampleDataProvider.CreateSampleValues().Select(value => new object[] {value});
}
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With