代码之家  ›  专栏  ›  技术社区  ›  Richard Ev

使用SETUPGET和SETUPSET模拟属性-这很有效,但为什么?

  •  21
  • Richard Ev  · 技术社区  · 14 年前

    使用moq我在嘲笑一个财产, Report TheReport { get; set; } 在接口上 ISessionData 这样我就可以检查在这个属性上设置的值。

    为了达到这个目的,我正在使用 SetupGet SetupSet 如下:

    // class-level fields
    protected Report _sessionReport;
    protected Mock<ISessionData> SessionData { get; private set; }
    

    在我的设置方法中…

    SessionData = new Mock<ISessionData>();
    
    SessionData
        .SetupSet(s => s.TheReport = It.IsAny<Report>())
        .Callback<RDLDesigner.Common.Report>(r =>
        {
            _sessionReport = r;
            SessionData.SetupGet(s => s.TheReport).Returns(_sessionReport);
        });
    

    我发现这个方法是在 StackOverflow 但我不明白为什么。我想打电话给 设置程序 在外面 设置镦粗 回调。

    有人能解释这种方法是如何工作的和为什么工作的吗?如果它是模拟这种类型的属性的最合适的方法的话?

    编辑

    使用 SessionData.SetupProperty(s => s.TheReport); 同样适用于我的场景,但是我仍然对我最初的方法如何工作以及为什么工作感兴趣。

    2 回复  |  直到 13 年前
        1
  •  30
  •   John Foster    14 年前

    调用SETUPGET时使用回调的原因是_sessionreport引用是按值传递的,这意味着对set方法的后续调用不会更新get方法返回的值。

    看清楚发生了什么。如果您设置了如下模拟模型:

    SessionData.SetupSet(s => s.Report = It.IsAny<Report>());
    SessionData.SetupGet(s => s.Report).Returns(_report);
    

    在伪代码中,模拟的实现看起来有点像

    public Report Report {
        set { }
        get { 
           // Copy of the value of the _report reference field in your test class
           return _reportCopy; 
        }  
    }
    

    所以这样做是行不通的:

     ISessionData session = SessionData.Object
     Report report = new Report();
     session.Report = report;
     session.Report.ShouldEqual(report); //Fails
     _report.ShouldEqual(report); // Fails
    

    显然,我们需要向set方法添加一些行为,因此我们像这样设置mock

    SessionData.SetupSet(s => s.Report = It.IsAny<Report>())
               .Callback(s => _report = s);
    SessionData.SetupGet(s => s.Report).Returns(_report);
    

    这导致模拟的实现看起来有点像

    public Report Report {
        set {
           // Invokes delegate that sets the field on test class
        }
        get { 
           // Copy of the original value of the _report reference field
           // in your test class
           return _reportCopy; 
        }  
    }
    

    但是,这会导致以下问题:

      ISessionData session = SessionData.Object
      Report report = new Report();
      session.Report = report;
      _report.ShouldEqual(report); // Passes
      session.Report.ShouldEqual(report); //Fails!
    

    本质上,属性上的“get”方法仍然返回对原始对象报告的引用,因为引用是通过值传递给SETUPGET方法的。

    因此,每次调用setter时,我们需要更新report getter返回的值,该值将指向原始代码。

    SessionData
       .SetupSet(s => s.TheReport = It.IsAny<Report>())
       .Callback<RDLDesigner.Common.Report>(r => {
            _sessionReport = r;
            SessionData.SetupGet(s => s.TheReport).Returns(_sessionReport);
       });
    

    这样可以确保get方法返回的值始终与上一次对set方法的调用保持同步。并导致(功能上)行为如下:

    public Report Report {
        set {
           // Sets the field on the test class
           _reportCopy = value;
        }
        get { 
           // Copy of the value of the _report reference field in your test class
           return _reportCopy; 
        }  
    }
    
        2
  •  10
  •   stoj    13 年前

    将SETUPGET放入SETUPSET实现中似乎非常/过于复杂。

    返回委托会更容易,因此每次都要对其进行评估,而不仅仅是返回引用的副本。

    像这样的东西在眼睛上要容易得多(而且应该表现得更好,因为你不需要不断地重新定义getter)。

    SessionData
       .SetupSet(s => s.TheReport = It.IsAny<Report>())
       .Callback<RDLDesigner.Common.Report>(r => _sessionReport = r);    
    SessionData
       .SetupGet(s => s.TheReport).Returns(() => _sessionReport);