Используя 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);
также работы в моем сценарии, но я все еще интересуюсь любыми объяснениями того, как и почему мой исходный подход работал.
Причина использования обратного вызова в вызове 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;
}
}