Using this generic base class:
public abstract class Logic<U> where U : class
{
protected U m_provider;
public Logic(U provider)
{
m_provider = provider;
}
}
I'm trying to create a base test class for unit test:
public class LogicBaseTest<T, U> where T : Logic <U>, new() where U: class
{
protected T m_logic;
protected U m_provider;
[OneTimeSetUp]
public virtual void OneTimeSetup()
{
m_provider = (U)Substitute.For<IInterface>();
m_logic = new T(m_provider);
}
}
It complains on the constructor, it requests for the new() constrain but when I add it then it complains that the constructor cannot take parameters.
I could add a method to populate the provider but I'm wondering whether it could be done in the constructor.
CodePudding user response:
So you have two problems here:
- LogicBaseTest needs to know how to instantiate a
Logic<U>
. Logic<U>
requires aU
in the constructor.
My proposed solution to it is to pass a factory delegate into the base test class and remove the new()
requirement. Then your setup can construct the Logic
class using the factory:
public class LogicBaseTest<T, U>
where T : Logic<U>
where U: class
{
protected readonly Func<U, T> _factory;
public LogicBaseTest(Func<U, T> factory)
{
_factory = factory;
}
[OneTimeSetUp]
public virtual void OneTimeSetup()
{
m_provider = (U)Substitute.For<IInterface>();
m_logic = _factory(m_provider);
}
}
In the derived test class you just have to tell the base class how to new up a Logic<U>
:
public class DerivedTest : LogicBaseTest<Logic<MyUType>, MyUType>
{
public DerivedTest()
: this(u => new Logic<MyUType>(u))
{
}
}
CodePudding user response:
Let's break down your generic constraint into plain English
where T : Logic<U>, new()
This means
The type of
T
needs to inherit fromLogic
, the generic type parameter must beU
and have a public, parameterless, constructor
But the problem is that Logic
by itself already breaks that constraint. Now, how do we fix this? There are multiple ways
Use a "factory function" to instantiate your
m_logic
alongside removing thenew
constraint (see DiplomacyNotWar's answer)Remove the
new()
constraint and use something likeActivator.CreateInstance
instead to instantiate yourLogic
classAdd a parameterless constructor to
Logic
and configure yourm_provider
some other wayMove instantiation of
m_logic
into the unit test itself (maybe add a helper method if you need to create the sameLogic
for a ton of unit tests)Research if your unit testing framework supports some form of Dependency Injection and inject everything you need
CodePudding user response:
You cannot add a generic type constraint such as where T : new(U)
.
Instead, you can use a Factory.
public interface IFactory<out TObject, in TProvider>
{
public TObject Create(TProvider provider);
}
then use it in your base test
public class LogicBaseTest<T, U> where T : Logic <U> where U: class // remove new()
{
// fields
private readonly IFactory<T, U> _factory;
public LogicBaseTest(IFactory<T, U> factory)
{
_factory = factory;
}
[OneTimeSetUp]
public virtual void OneTimeSetup()
{
m_provider = (U)Substitute.For<IInterface>();
m_logic = _factory.Create(m_provider);
}
}
Example
public class Logic1Provider
{
}
public class Logic1Factory : IFactory<Logic1, Logic1Provider>
{
public Logic1 Create(Logic1Provider provider)
{
return new Logic1(provider);
}
}
public class Logic1 : Logic<Logic1Provider>
{
public Logic1(Logic1Provider provider) : base(provider)
{
}
public void DoDomeLogic()
{
// do stuff
}
}
var factory = new Logic1Factory();
var baseTest = new LogicBaseTest<Logic1, Logic1Provider>(factory);