Contract.Requires抛出pex错误

26

可能重复的问题:
如何配置Pex以遵守代码合同?

目前,当我运行pex探索时,我的类中创建的代码合同在pex探索结果中被视为错误。我认为当使用代码合同运行pex探索时,合同失败应该被视为预期行为。以下是导致异常的代码。

测试方法:

[PexMethod]
public void TestEquality(Guid userId, string username, string password, string securityQuestion, string securityAnswer)
{
    UserSecurity user = UserTools.CreateUser(Guid.NewGuid(), username, password, securityQuestion, securityAnswer);

    bool passwordResult = UserTools.VerifyInput(password, user.Password, user.PasswordSalt);
    bool securityAnswerResult = UserTools.VerifyInput(securityAnswer, user.SecurityAnswer, user.SecurityAnswerSalt);

    Assert.IsTrue(passwordResult, "Password did not correctly re-hash");
    Assert.IsTrue(securityAnswerResult, "Security Answer did not correctly re-hash");
}

方法调用失败:

public static UserSecurity CreateUser(Guid userId, string username, string password, string securityQuestion, string securityAnswer)
{
    Contract.Requires(userId != Guid.Empty);
    Contract.Requires(!string.IsNullOrWhiteSpace(username));
    Contract.Requires(!string.IsNullOrWhiteSpace(password));
    Contract.Requires(!string.IsNullOrWhiteSpace(securityQuestion));
    Contract.Requires(!string.IsNullOrWhiteSpace(securityAnswer));
    Contract.Ensures(Contract.Result<UserSecurity>() != null);

    byte[] passwordSalt;
    byte[] securityAnswerSalt;

    return new UserSecurity
               {
                   UserId = userId,
                   Username = username,
                   Password = SecurityUtilities.GenerateHash(password, out passwordSalt),
                   PasswordSalt = passwordSalt,
                   SecurityQuestion = securityQuestion,
                   SecurityAnswer = SecurityUtilities.GenerateHash(securityAnswer, out securityAnswerSalt),
                   SecurityAnswerSalt = securityAnswerSalt,
               };
}

--- Description

failing test: ContractException, Precondition failed: !string.IsNullOrWhiteSpace(username)

Guid s0
   = new Guid(default(int), (short)32, (short)32, default(byte), default(byte), 
              default(byte), default(byte), default(byte), 
              default(byte), default(byte), default(byte));
this.TestEquality(s0, (string)null, (string)null, (string)null, (string)null);


[TestMethod]
[PexGeneratedBy(typeof(HashTests))]
[PexRaisedContractException]
public void TestEqualityThrowsContractException173()
{
    Guid s0
       = new Guid(default(int), (short)32, (short)32, default(byte), default(byte), 
                  default(byte), default(byte), default(byte), 
                  default(byte), default(byte), default(byte));
    this.TestEquality(s0, (string)null, (string)null, (string)null, (string)null);
}

PEX团队是否在监控这个论坛?还是说PEX团队已经不存在了? - Joshua Dale
1
我不会称呼这个为“pex论坛”,尽管“他们”中的某人可能会在这里查看。看起来这里才是论坛。 - Christian.K
1
我认为他们不再在那里回复了。在 Pex 主页上,他们注明论坛已经迁移到 StackOverflow 上。Pex 主页 - Joshua Dale
哎呀,应该更加注意。在旧论坛中甚至有一篇关于迁移到SO的帖子。抱歉。 - Christian.K
一切都很好!我甚至找不到他们的旧论坛,所以谢谢你。 :) - Joshua Dale
2个回答

0

我发现如果您使用标准的合同重写器,在失败时取消勾选assert并使用类型化的Requires参数让您的代码抛出ArgumentNullException。

contract.Requires<ArgumentNullException>(i!=null);

当您执行此操作时,方法将抛出argumentnullexceptions...pex与它们的表现非常好。

在编译时,您仍然会得到合同检查和静态检查,就像您所期望的那样。

看起来PexRaisedContractException在您使用它时并没有按照预期的方式运行。虽然我不能说我使用过那个属性。我想从您的角度来看,我的方法是一个解决方法;)

编辑:Pex应该生成这个测试,但测试应该抛出错误,这应该导致测试通过。事实上,这不起作用表明重写器没有工作或者被抛出的异常不是属性正在寻找的异常类型。


当使用代码合同和 Pex 时,Pex 将合同失败视为预期的异常,并在探索中将其标记为绿色。你的修复确实起作用了,但是在同时使用 Pex 和代码合同时没有任何好处。 - Joshua Dale
你从使用Pex和Contracts中获得不了任何好处,这话是胡说八道。在我使用这两种技术的方式中,它们对我的代码质量来说是最大的进步。对于那些无法让其正常工作的人来说,这是相当教条主义的说法。即使您意味着额外的好处,我仍然不同意,因为合同给我编译时检查,而Pex为我提供了一个用于探索性测试的强大工具。我已经厌倦了为null编写测试... - John Nicholas
对不起,我并不是要贬低你的修复方法,它完全有效。我只是在说它现在的表现和以前不一样了。而且现在与 PEX 团队联系获取支持比以前更加困难。 - Joshua Dale
完全同意支持部分;)我花了一段时间说服人们pex并没有死...但是由于代码合同静态检查的高级要求,这种东西很难推销。 - John Nicholas
同意。这是一个缺乏透明度的好产品。我听说Moles正在拖延pex更新,但我不记得在哪里听到的。 - Joshua Dale

0

从我有限的Pex使用经验来看,我的理解是Contract方法定义了到达它们所在方法的前提条件。因此,当你说

Contract.Requires(!string.IsNullOrWhiteSpace(username));

你说不可能通过空或空格的用户名参数来达到该语句。Pex基本上是在说你错了。这是Pex真正擅长的一件事情。这意味着你有可能会出现NullReferenceException,或者在调用CreateUser方法时没有检查空/空格的username。那么你的任务就是找到问题所在。你可以通过处理CreateUser方法中的null/空格username并消除它的Contract.Requires来解决问题,或者确保所有调用CreateUser的调用方都传递非空、非空格的用户名。我认为更好的选择取决于你的情况,但在几乎所有情况下,我会在CreateUser方法中处理null/空格的用户名。这样,你就可以在代码中的一个地方优雅地处理错误。

当然,你真的应该看看哪个调用方可以传递null或空格,因为这可能表明存在用户输入验证问题,以及其他潜在问题。


这对于代码合同是正确的。然而,当您使用PEX执行参数化单元测试时,它应该将代码合同视为预期行为。因此,即使合同在运行时抛出异常,pex也会将此异常视为预期结果。这曾经是pex单元测试的情况。我认为这可能是探索结果的一个错误。 - Joshua Dale
@Joshua Dale 请参阅 http://research.microsoft.com/en-us/projects/pex/pexandcontracts.pdf 的第10-11页。您的“执行运行时合同检查”设置是什么? - Andrew
合同已为目标项目启用(我还为测试项目启用了它,以确保一切正常)。此外,我目前遇到了一个ContractException异常,因此代码合同已启用。 - Joshua Dale
我强烈建议不要在CreateUser方法中处理null/空格。该方法无法在没有有效信息的情况下执行其工作,因此仍需要抛出异常 - 这正是首先定义前置条件的理想情况。 - Morten Mertner
@Morten Mertner,我不确定你在说什么。为什么我不能在那个方法中放置一个前置条件呢? - Joshua Dale

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接