gpt4 book ai didi

c# - 如何告诉 Pex 不要 stub 具有具体实现的抽象类

转载 作者:IT王子 更新时间:2023-10-29 03:45:50 28 4
gpt4 key购买 nike

我正在尝试使用 Pex 来测试一些代码。我有一个具有四个具体实现的抽象类。我为四种具体类型中的每一种都创建了工厂方法。我还为抽象类型创建了一个,除了 this nice thread。说明,Pex 不会,也不应该使用抽象工厂方法。

问题是我的一些代码依赖于所有的四种具体类型(因为创建更多子类的可能性非常小),但是 Pex 通过使用 Moles 创建一个 stub 。

我如何强制 Pex 使用其中一种工厂方法(任何一种,我不在乎)来创建抽象类的实例,而无需为该抽象类创建 Moles stub ?是否有 PexAssume 指令可以完成此操作?请注意,一些具体类型形成了一种树结构类型,所以说 ConcreteImplementation 派生自 AbstractClass,而 ConcreteImplementation 有两个类型的属性 抽象类。我需要确保根本没有在树中的任何地方使用 stub 。 (并非所有具体实现都具有 AbstractClass 属性。)

编辑:

看来我需要添加更多关于类结构本身如何工作的信息,但请记住,目标仍然是如何让 Pex 不 stub 类。

这里是抽象基类的简化版本及其四个具体实现。

public abstract class AbstractClass
{
public abstract AbstractClass Distill();

public static bool operator ==(AbstractClass left, AbstractClass right)
{
// some logic that returns a bool
}

public static bool operator !=(AbstractClass left, AbstractClass right)
{
// some logic that basically returns !(operator ==)
}

public static Implementation1 Implementation1
{
get
{
return Implementation1.GetInstance;
}
}
}

public class Implementation1 : AbstractClass, IEquatable<Implementation1>
{
private static Implementation1 _implementation1 = new Implementation1();

private Implementation1()
{
}

public override AbstractClass Distill()
{
return this;
}

internal static Implementation1 GetInstance
{
get
{
return _implementation1;
}
}

public bool Equals(Implementation1 other)
{
return true;
}
}

public class Implementation2 : AbstractClass, IEquatable<Implementation2>
{
public string Name { get; private set; }
public string NamePlural { get; private set; }

public Implementation2(string name)
{
// initializes, including
Name = name;
// and sets NamePlural to a default
}

public Implementation2(string name, string plural)
{
// initializes, including
Name = name;
NamePlural = plural;
}

public override AbstractClass Distill()
{
if (String.IsNullOrEmpty(Name))
{
return AbstractClass.Implementation1;
}
return this;
}

public bool Equals(Implementation2 other)
{
if (other == null)
{
return false;
}

return other.Name == this.Name;
}
}

public class Implementation3 : AbstractClass, IEquatable<Implementation3>
{
public IEnumerable<AbstractClass> Instances { get; private set; }

public Implementation3()
: base()
{
Instances = new List<AbstractClass>();
}

public Implementation3(IEnumerable<AbstractClass> instances)
: base()
{
if (instances == null)
{
throw new ArgumentNullException("instances", "error msg");
}

if (instances.Any<AbstractClass>(c => c == null))
{
thrown new ArgumentNullException("instances", "some other error msg");
}

Instances = instances;
}

public override AbstractClass Distill()
{
IEnumerable<AbstractClass> newInstances = new List<AbstractClass>(Instances);

// "Flatten" the collection by removing nested Implementation3 instances
while (newInstances.OfType<Implementation3>().Any<Implementation3>())
{
newInstances = newInstances.Where<AbstractClass>(c => c.GetType() != typeof(Implementation3))
.Concat<AbstractClass>(newInstances.OfType<Implementation3>().SelectMany<Implementation3, AbstractUnit>(i => i.Instances));
}

if (newInstances.OfType<Implementation4>().Any<Implementation4>())
{
List<AbstractClass> denominator = new List<AbstractClass>();

while (newInstances.OfType<Implementation4>().Any<Implementation4>())
{
denominator.AddRange(newInstances.OfType<Implementation4>().Select<Implementation4, AbstractClass>(c => c.Denominator));
newInstances = newInstances.Where<AbstractClass>(c => c.GetType() != typeof(Implementation4))
.Concat<AbstractClass>(newInstances.OfType<Implementation4>().Select<Implementation4, AbstractClass>(c => c.Numerator));
}

return (new Implementation4(new Implementation3(newInstances), new Implementation3(denominator))).Distill();
}

// There should only be Implementation1 and/or Implementation2 instances
// left. Return only the Implementation2 instances, if there are any.
IEnumerable<Implementation2> i2s = newInstances.Select<AbstractClass, AbstractClass>(c => c.Distill()).OfType<Implementation2>();
switch (i2s.Count<Implementation2>())
{
case 0:
return AbstractClass.Implementation1;
case 1:
return i2s.First<Implementation2>();
default:
return new Implementation3(i2s.OrderBy<Implementation2, string>(c => c.Name).Select<Implementation2, AbstractClass>(c => c));
}
}

public bool Equals(Implementation3 other)
{
// omitted for brevity
return false;
}
}

public class Implementation4 : AbstractClass, IEquatable<Implementation4>
{
private AbstractClass _numerator;
private AbstractClass _denominator;

public AbstractClass Numerator
{
get
{
return _numerator;
}

set
{
if (value == null)
{
throw new ArgumentNullException("value", "error msg");
}

_numerator = value;
}
}

public AbstractClass Denominator
{
get
{
return _denominator;
}

set
{
if (value == null)
{
throw new ArgumentNullException("value", "error msg");
}
_denominator = value;
}
}

public Implementation4(AbstractClass numerator, AbstractClass denominator)
: base()
{
if (numerator == null || denominator == null)
{
throw new ArgumentNullException("whichever", "error msg");
}

Numerator = numerator;
Denominator = denominator;
}

public override AbstractClass Distill()
{
AbstractClass numDistilled = Numerator.Distill();
AbstractClass denDistilled = Denominator.Distill();

if (denDistilled.GetType() == typeof(Implementation1))
{
return numDistilled;
}
if (denDistilled.GetType() == typeof(Implementation4))
{
Implementation3 newInstance = new Implementation3(new List<AbstractClass>(2) { numDistilled, new Implementation4(((Implementation4)denDistilled).Denominator, ((Implementation4)denDistilled).Numerator) });
return newInstance.Distill();
}
if (numDistilled.GetType() == typeof(Implementation4))
{
Implementation4 newImp4 = new Implementation4(((Implementation4)numReduced).Numerator, new Implementation3(new List<AbstractClass>(2) { ((Implementation4)numDistilled).Denominator, denDistilled }));
return newImp4.Distill();
}

if (numDistilled.GetType() == typeof(Implementation1))
{
return new Implementation4(numDistilled, denDistilled);
}

if (numDistilled.GetType() == typeof(Implementation2) && denDistilled.GetType() == typeof(Implementation2))
{
if (((Implementation2)numDistilled).Name == (((Implementation2)denDistilled).Name)
{
return AbstractClass.Implementation1;
}
return new Implementation4(numDistilled, denDistilled);
}

// At this point, one or both of numerator and denominator are Implementation3
// instances, and the other (if any) is Implementation2. Because both
// numerator and denominator are distilled, all the instances within either
// Implementation3 are going to be Implementation2. So, the following should
// work.
List<Implementation2> numList =
numDistilled.GetType() == typeof(Implementation2) ? new List<Implementation2>(1) { ((Implementation2)numDistilled) } : new List<Implementation2>(((Implementation3)numDistilled).Instances.OfType<Implementation2>());

List<Implementation2> denList =
denDistilled.GetType() == typeof(Implementation2) ? new List<Implementation2>(1) { ((Implementation2)denDistilled) } : new List<Implementation2>(((Implementation3)denDistilled).Instances.OfType<Implementation2>());

Stack<int> numIndexesToRemove = new Stack<int>();
for (int i = 0; i < numList.Count; i++)
{
if (denList.Remove(numList[i]))
{
numIndexesToRemove.Push(i);
}
}

while (numIndexesToRemove.Count > 0)
{
numList.RemoveAt(numIndexesToRemove.Pop());
}

switch (denList.Count)
{
case 0:
switch (numList.Count)
{
case 0:
return AbstractClass.Implementation1;
case 1:
return numList.First<Implementation2>();
default:
return new Implementation3(numList.OfType<AbstractClass>());
}
case 1:
switch (numList.Count)
{
case 0:
return new Implementation4(AbstractClass.Implementation1, denList.First<Implementation2>());
case 1:
return new Implementation4(numList.First<Implementation2>(), denList.First<Implementation2>());
default:
return new Implementation4(new Implementation3(numList.OfType<AbstractClass>()), denList.First<Implementation2>());
}
default:
switch (numList.Count)
{
case 0:
return new Implementation4(AbstractClass.Implementation1, new Implementation3(denList.OfType<AbstractClass>()));
case 1:
return new Implementation4(numList.First<Implementation2>(), new Implementation3(denList.OfType<AbstractClass>()));
default:
return new Implementation4(new Implementation3(numList.OfType<AbstractClass>()), new Implementation3(denList.OfType<AbstractClass>()));
}
}
}

public bool Equals(Implementation4 other)
{
return Numerator.Equals(other.Numerator) && Denominator.Equals(other.Denominator);
}
}

我要测试的核心是 Distill 方法,如您所见,它具有递归运行的潜力。因为 stub 的 AbstractClass 在此范例中毫无意义,它破坏了算法逻辑。即使尝试测试 stub 类也有点无用,因为除了抛出异常或假装它是 Implementation1 的实例之外,我对此无能为力。我宁愿不必以这种方式重写被测代码以适应特定的测试框架,而是以一种永远不会对 AbstractClass 进行 stub 的方式编写测试本身是我想要做的在这里。

例如,我希望我所做的与类型安全的枚举结构有何不同是显而易见的。另外,我将对象匿名化以便在此处发布(如您所知),并且我没有包括所有方法,因此如果您要评论告诉我 Implementation4.Equals(Implementation4) 已损坏,别担心,我知道这里有问题,但我的实际代码会解决这个问题。

另一个编辑:

这是其中一个工厂类的示例。它位于 Pex 生成的测试项目的 Factories 目录中。

public static partial class Implementation3Factory
{
[PexFactoryMethod(typeof(Implementation3))]
public static Implementation3 Create(IEnumerable<AbstractClass> instances, bool useEmptyConstructor)
{
Implementation3 i3 = null;
if (useEmptyConstructor)
{
i3 = new Implementation3();
}
else
{
i3 = new Implementation3(instances);
}

return i3;
}
}

在我的这些具体实现的工厂方法中,可以使用任何构造函数来创建具体实现。在示例中,useEmptyConstructor 参数控制要使用的构造函数。其他工厂方法也有类似的特点。我记得读过这些工厂方法应该允许在每种可能的配置中创建对象,但我无法立即找到链接。

最佳答案

您是否尝试过使用 [PexUseType] 属性告诉 Pex,您的抽象类存在非抽象子类型?如果 Pex 不知道任何非抽象子类型,那么 Pex 的约束求解器将确定依赖于非抽象子类型存在的代码路径是不可行的。

关于c# - 如何告诉 Pex 不要 stub 具有具体实现的抽象类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7505030/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com