In an attempt to try to understand what you want to achieve, I came up with the below. Don't consider it as a review to make your code better - but just as an attempt to think along the paths of yours and t3chb0ts:
public abstract class TesterBase
{
public virtual void NullArgumentShouldThrow<TIn, TOut>(Func<TIn, TOut> function)
{
if (typeof(TIn).IsClass)
{
Assert.ThrowsException<ArgumentNullException>(() => function(default(TIn)), "Should throw ArgumentNullException");
}
}
}
public abstract class IBuilderTesterBase<TBuilder, TIn, TOut> : TesterBase where TBuilder : IBuilder<TIn, TOut>, new()
{
protected abstract TBuilder Create() => new TBuilder();
[TestMethod]
public void TestBuildArgumentNullException()
{
var builder = Create();
NullArgumentShouldThrow<TIn, TOut>(builder.Build);
}
// TODO: Other common IBuilder Tests
}
public abstract class SimpleIBuilderTesterBase<TBuilder, TIn, TOut> : IBuilderTesterBase<TBuilder, TIn, TOut> where TBuilder : IBuilder<TIn, TOut>, new()
{
protected override TBuilder Create()
{
return new TBuilder();
}
}
public abstract class ComplexIBuilderTesterBase<TBuilder, TIn, TOut> : IBuilderTesterBase<TBuilder, TIn, TOut> where TBuilder : IBuilder<TIn, TOut>
{
Func<TBuilder> m_creator;
public ComplexIBuilderTesterBase(Func<TBuilder> creator)
{
m_creator = creator;
}
protected override TBuilder Create()
{
return m_creator();
}
}
[TestClass]
public class ObjectBuilderTester : IBuilderTesterBase<ReferenceTypeBuilderSimpleIBuilderTesterBase<ReferenceTypeBuilder, object, int>
{
// TODO: Other ObjectBuilder tests
}
[TestClass]
public class DateTimeBuilderTester : IBuilderTesterBase<ValueTypeBuilderSimpleIBuilderTesterBase<ValueTypeBuilder, DateTime, int>
{
// TODO: Other DateTimeBuilder tests
}
[TestClass]
public class ComplexBuilderTester : ComplexIBuilderTesterBase<ComplexBuilder, object, int>
{
public ComplexBuilderTester() : base(DoCreate)
{
}
private static ComplexBuilder DoCreate()
{
return new ComplexBuilder(null);
}
// TODO: Other ComplexBuilder tests
}
I admit that it maybe doesn't add any clarity to the picture, but I think it gets the job done.