我无法让PEX自动覆盖调用Linq扩展方法的方法,例如本例中的Where()和Contains():
public class MyEntity
{
public int Id { get; set; }
}
public interface IWithQueryable
{
IQueryable<MyEntity> QueryableSet();
}
public class ConsumerOfIhaveIQueryable
{
private readonly IWithQueryable _withIQueryable;
public ConsumerOfIhaveIQueryable(IWithQueryable withIQueryable)
{
// <pex>
Contract.Requires<ArgumentNullException>(
withIQueryable != null, "withIQueryable");
// </pex>
_withIQueryable = withIQueryable;
}
public IEnumerable<MyEntity> GetEntitiesByIds(IEnumerable<int> ids)
{
Contract.Requires<ArgumentNullException>(ids != null, "ids");
// <pex>
Contract.Assert
(this._withIQueryable.QueryableSet() != (IQueryable<MyEntity>)null);
// </pex>
IEnumerable<MyEntity> entities =
_withIQueryable.QueryableSet().Where(
entity => ids.Contains(entity.Id));
if (entities.Count() != ids.Count())
{
return null;
}
return entities;
}
}
[PexClass(typeof(ConsumerOfIhaveIQueryable))]
[PexAllowedExceptionFromTypeUnderTest(typeof(InvalidOperationException))]
[PexAllowedExceptionFromTypeUnderTest(typeof(ArgumentException), AcceptExceptionSubtypes = true)]
[TestClass]
public partial class ConsumerOfIhaveIQueryableTest
{
[PexMethod]
public IEnumerable<MyEntity> GetEntitiesByIds(
[PexAssumeUnderTest]ConsumerOfIhaveIQueryable target,
int[] ids)
{
var result = target.GetEntitiesByIds(ids);
PexAssert.IsTrue(result.Count() == ids.Length);
return result;
}
}
当我在这个PexMethod上运行PEX探索时,我发现了以下问题:
---描述 测试失败:ArgumentNullException,Value不能为null。 参数名称:source
[TestMethod]
[PexGeneratedBy(typeof(ConsumerOfIhaveIQueryableTest))]
[PexRaisedException(typeof(ArgumentNullException))]
public void GetEntitiesByIdsThrowsArgumentNullException385()
{
using (PexChooseBehavedBehavior.Setup())
{
SIWithQueryable sIWithQueryable;
ConsumerOfIhaveIQueryable consumerOfIhaveIQueryable;
IEnumerable<MyEntity> iEnumerable;
sIWithQueryable = new SIWithQueryable();
consumerOfIhaveIQueryable =
ConsumerOfIhaveIQueryableFactory.Create((IWithQueryable)sIWithQueryable);
int[] ints = new int[0];
iEnumerable = this.GetEntitiesByIds(consumerOfIhaveIQueryable, ints);
}
}
---例外细节
System.ArgumentNullException:值不能为null。 参数名称:System.Linq.IQueryable'1 System.Linq.Queryable.Where(System.Linq.IQueryable'1 source,System.Linq.Expressions.Expression'1&gt;谓词)中的source c:\ users \ moran \ documents \ visual studio 2010 \ Projects \ PexTuts \ PexIQueryable \ PexIQueryable \ ConsumerOfIhaveIQueryable.cs(29):at System.Collections.Generic.IEnumerable'1 PexIQueryable.ConsumerOfIhaveIQueryable.GetEntitiesByIds(System.Collections.Generic。 IEnumerable`1 ids) c:\ users \ moran \ documents \ visual studio 2010 \ Projects \ PexTuts \ PexIQueryable \ PexIQueryable.Tests \ ConsumerOfIhaveIQueryableTest.cs(34):at System.Collections.Generic.IEnumerable'1 PexIQueryable.ConsumerOfIhaveIQueryableTest.GetEntitiesByIds(PexIQueryable.ConsumerOfIhaveIQueryable target) ,System.Int32 [] ids)
很想对此发表评论......
顺便说一句,这是我的第一篇SO帖子,希望我没有太多代码和信息的垃圾邮件。Moran的
答案 0 :(得分:1)
在为此设置代码一段时间后,我做了一些假设。我假设您通过Moles存根对IWithQueryable进行了存根,并且当您删除了QueryableSet()方法不返回null的Contract断言时也会发生NullArgumentException。
对于代码来说,IMO代码越多越好,只要它相关 - 就好了太多而不能继续下去,所以没关系。如上所述,请尽量明确代码中的所有假设(例如Moles stubbing(因为有不同的方法来实现这一点,这是人们必须承担的)。
我不是100%肯定你在问什么。代码失败,因为存根IWithQueryable object
没有QueryableSet()
方法的实现,并且该方法返回null
。这里的PexAssert
无法帮助它弄清楚如何创建一个LINQ提供程序,这就是你要求它做的事情。 PexChooseBehavedBehavior.Setup()
只是替换对Moles存根(没有自定义委托)的委托的任何调用,其默认行为为default(T)
,这就是source
为空的原因 - QueryableSet()
初始化为null
。
您可以通过几种方式解决此问题(至少在提供创建QueryableSet()
方法的方法的意义上)。您可以创建工厂方法来生成整个SIWithQueryable
或仅QueryableSet
委托。这是Pex建议的东西(然而,在我看来它的类型和名称空间混乱了)。例如:
/// <summary>A factory for Microsoft.Moles.Framework.MolesDelegates+Func`1[System.Linq.IQueryable`1[StackOverflow.Q9968801.MyEntity]] instances</summary>
public static partial class MolesDelegatesFactory
{
/// <summary>A factory for Microsoft.Moles.Framework.MolesDelegates+Func`1[System.Linq.IQueryable`1[StackOverflow.Q9968801.MyEntity]] instances</summary>
[PexFactoryMethod(typeof(MolesDelegates.Func<IQueryable<MyEntity>>))]
public static MolesDelegates.Func<IQueryable<MyEntity>> CreateFunc()
{
throw new InvalidOperationException();
// TODO: Edit factory method of Func`1<IQueryable`1<MyEntity>>
// This method should be able to configure the object in all possible ways.
// Add as many parameters as needed,
// and assign their values to each field by using the API.
}
/// <summary>A factory for Microsoft.Moles.Framework.MolesDelegates+Func`1[System.Linq.IQueryable`1[StackOverflow.Q9968801.MyEntity]] instances</summary>
[PexFactoryMethod(typeof(SIWithQueryable))]
public static SIWithQueryable Create()
{
var siWithQueryable = new SIWithQueryable();
siWithQueryable.QueryableSet = () => { throw new InvalidOperationException(); };
return siWithQueryable;
// TODO: Edit factory method of Func`1<IQueryable`1<MyEntity>>
// This method should be able to configure the object in all possible ways.
// Add as many parameters as needed,
// and assign their values to each field by using the API.
}
}
然后将其连接到测试方法,其中两行之一分配sIWithQueryable
:
[TestMethod]
[PexGeneratedBy(typeof(ConsumerOfIhaveIQueryableTest))]
public void GetEntitiesByIdsThrowsArgumentNullException678()
{
SIWithQueryable sIWithQueryable;
// Either this for the whole object.
sIWithQueryable = MolesDelegatesFactory.Create();
// Or this for just that delegate.
sIWithQueryable = new SIWithQueryable();
sIWithQueryable.QueryableSet = MolesDelegatesFactory.CreateFunc();
ConsumerOfIhaveIQueryable consumerOfIhaveIQueryable;
IEnumerable<MyEntity> iEnumerable;
consumerOfIhaveIQueryable = ConsumerOfIhaveIQueryableFactory.Create((IWithQueryable) sIWithQueryable);
int[] ints = new int[0];
iEnumerable = this.GetEntitiesByIds(consumerOfIhaveIQueryable, ints);
}
这将在为IWithQueryable
创建存根时调用工厂方法。这仍然是一个问题,因为重新启动探测将消除存根设置。
如果您提供无参数工厂方法来创建存根(MolesDelegatesFactory.CreateFunc()
),那么Pex将了解这一点并生成测试以使用它。因此,它将正确管理测试重新生成的行为。不幸的是,Pex建议将此委托创建为工厂方法 - 但是,它永远不会被调用,默认实现总是被使用,似乎必须模拟父类型。
但是,我想知道你为什么要创建一个简单包装另一个的接口IWithQueryable
,以及你期望用IQueryable
做什么。为了做一些非常有用的事情,你需要做很多工作来处理IQueryable
界面 - 主要是Provider
和Expression
,你会非常喜欢写一个模拟查询提供程序,这并不容易。