При насмешке использования свойства SetupGet и SetupSet - это работает, но почему?

Используя Moq я дразню свойство, Report TheReport { get; set; } в интерфейсе ISessionData так, чтобы я мог осмотреть значение, которое установлено на этом свойстве.

Для достижения этого, я использую SetupGet и SetupSet следующим образом:

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

И в моем методе установки...

SessionData = new Mock();

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

Я нашел этот подход к StackOverflow, и он работает, но я не понимаю почему. Я ожидал иметь вызов к SetupGet за пределами SetupSet обратный вызов.

Кто-либо может объяснить, как и почему этот подход работает, и если это - самый соответствующий способ дразнить свойство этого типа?

Править

Используя SessionData.SetupProperty(s => s.TheReport); также работы в моем сценарии, но я все еще интересуюсь любыми объяснениями того, как и почему мой исходный подход работал.

23
задан Community 23 May 2017 в 12:31
поделиться

1 ответ

Причина использования обратного вызова в вызове 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, поэтому мы настроим Макет следующим образом

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" на свойстве все еще возвращает ссылку на исходный объект, на который указывал _report, поскольку ссылка была передана по значению в метод SetupGet.

Поэтому нам нужно обновлять значение, возвращаемое геттером отчета, каждый раз, когда вызывается сеттер, что приводит к вашему оригинальному коду

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; 
    }  
}
32
ответ дан 29 November 2019 в 01:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: