如何使用 Moq 验证是否使用预期对象和 属性 名称调用了 PropertyChanged?
How to use Moq to Verify that PropertyChanged is invoked with the expected object and property name?
使用 NUnit 和 Moq,我想用 Moq 替换以下测试中的 PropertyChanged 处理程序代码,如果这样可以使测试更短更清晰。我目前不清楚如何在 Moq 中执行此操作,验证是否为 IsDirty 调用了一次 PropertyChanged,为 LocalDatabaseFilePath 调用了一次,每次都使用预期的对象(代码中的 o)。有人可以建议如何使用最小起订量做到这一点吗?
[Test]
[Category("Fast Tests")]
[Category("PropertyChanged Events")]
public void FactoryResetCommand_AllPropertiesChangedInViewModel_PropertyChangedEventsFiredAsExpected()
{
// Arrange
string expectedLocalDatabaseFilePath = "eldfp";
string otherLocalDatabaseFilePath = "other" + expectedLocalDatabaseFilePath;
Mock<IDataStoreSettingsDataModel> stubDataModel = new Mock<IDataStoreSettingsDataModel>();
stubDataModel.Setup(x => x.LocalDatabaseFilePath).Returns(expectedLocalDatabaseFilePath);
IDataStoreSettingsViewModel sutViewModel = new DataStoreSettingsViewModel(
stubDataModel.Object,
ReportExceptionAsync);
sutViewModel.LocalDatabaseFilePath = otherLocalDatabaseFilePath;
sutViewModel.IsDirty = false;
// I'd like to replace the following by Moq if shorter/clearer
int propertyChangedCountIsDirty = 0;
int propertyChangedCountLocalDatabaseFilePath = 0;
object? objIsDirty = null;
object? objLocalDatabaseFilePath = null;
sutViewModel.PropertyChanged += ((o, e) =>
{
switch (e?.PropertyName)
{
case nameof(DataStoreSettingsViewModel.IsDirty):
objIsDirty = o;
++propertyChangedCountIsDirty;
break;
case nameof(DataStoreSettingsViewModel.LocalDatabaseFilePath):
objLocalDatabaseFilePath = o;
++propertyChangedCountLocalDatabaseFilePath;
break;
}
});
// I'd like to replace the above by Moq if shorter/clearer
// Act
if (sutViewModel.FactoryResetCommand.CanExecute(null))
sutViewModel.FactoryResetCommand.Execute(null);
// Assert
Assert.AreEqual(1, propertyChangedCountIsDirty);
Assert.AreEqual(1, propertyChangedCountLocalDatabaseFilePath);
Assert.AreSame(sutViewModel, objIsDirty);
Assert.AreSame(sutViewModel, objLocalDatabaseFilePath);
}
自己解决了。
添加如下界面后:
public interface IPropertyChangedEventHandler
{
void PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e);
}
使用 Moq 的测试如下所示:
[Test]
[Category("Fast Tests")]
[Category("PropertyChanged Events")]
public void FactoryResetCommand_AllPropertiesChangedInViewModel_PropertyChangedEventsFiredAsExpected()
{
// Arrange
string originalLocalDatabaseFilePath = "oldfp";
string otherLocalDatabaseFilePath = "other" + originalLocalDatabaseFilePath;
Mock<IPropertyChangedEventHandler> mockPropertyChangedEventHandler = new Mock<IPropertyChangedEventHandler>();
Mock<IDataStoreSettingsDataModel> stubDataModel = new Mock<IDataStoreSettingsDataModel>();
stubDataModel.Setup(x => x.LocalDatabaseFilePath).Returns(originalLocalDatabaseFilePath);
IDataStoreSettingsViewModel sutViewModel = new DataStoreSettingsViewModel(
stubDataModel.Object,
ReportExceptionAsync);
sutViewModel.LocalDatabaseFilePath = otherLocalDatabaseFilePath;
sutViewModel.IsDirty = false;
sutViewModel.PropertyChanged += mockPropertyChangedEventHandler.Object.PropertyChanged;
// Act
if (sutViewModel.FactoryResetCommand.CanExecute(null))
sutViewModel.FactoryResetCommand.Execute(null);
// Assert
mockPropertyChangedEventHandler.Verify(x => x.PropertyChanged(sutViewModel,
It.Is<System.ComponentModel.PropertyChangedEventArgs>(e => e.PropertyName == nameof(DataStoreSettingsViewModel.IsDirty))),
Times.Once);
mockPropertyChangedEventHandler.Verify(x => x.PropertyChanged(sutViewModel,
It.Is<System.ComponentModel.PropertyChangedEventArgs>(e => e.PropertyName == nameof(DataStoreSettingsViewModel.LocalDatabaseFilePath))),
Times.Once);
}
使用 NUnit 和 Moq,我想用 Moq 替换以下测试中的 PropertyChanged 处理程序代码,如果这样可以使测试更短更清晰。我目前不清楚如何在 Moq 中执行此操作,验证是否为 IsDirty 调用了一次 PropertyChanged,为 LocalDatabaseFilePath 调用了一次,每次都使用预期的对象(代码中的 o)。有人可以建议如何使用最小起订量做到这一点吗?
[Test]
[Category("Fast Tests")]
[Category("PropertyChanged Events")]
public void FactoryResetCommand_AllPropertiesChangedInViewModel_PropertyChangedEventsFiredAsExpected()
{
// Arrange
string expectedLocalDatabaseFilePath = "eldfp";
string otherLocalDatabaseFilePath = "other" + expectedLocalDatabaseFilePath;
Mock<IDataStoreSettingsDataModel> stubDataModel = new Mock<IDataStoreSettingsDataModel>();
stubDataModel.Setup(x => x.LocalDatabaseFilePath).Returns(expectedLocalDatabaseFilePath);
IDataStoreSettingsViewModel sutViewModel = new DataStoreSettingsViewModel(
stubDataModel.Object,
ReportExceptionAsync);
sutViewModel.LocalDatabaseFilePath = otherLocalDatabaseFilePath;
sutViewModel.IsDirty = false;
// I'd like to replace the following by Moq if shorter/clearer
int propertyChangedCountIsDirty = 0;
int propertyChangedCountLocalDatabaseFilePath = 0;
object? objIsDirty = null;
object? objLocalDatabaseFilePath = null;
sutViewModel.PropertyChanged += ((o, e) =>
{
switch (e?.PropertyName)
{
case nameof(DataStoreSettingsViewModel.IsDirty):
objIsDirty = o;
++propertyChangedCountIsDirty;
break;
case nameof(DataStoreSettingsViewModel.LocalDatabaseFilePath):
objLocalDatabaseFilePath = o;
++propertyChangedCountLocalDatabaseFilePath;
break;
}
});
// I'd like to replace the above by Moq if shorter/clearer
// Act
if (sutViewModel.FactoryResetCommand.CanExecute(null))
sutViewModel.FactoryResetCommand.Execute(null);
// Assert
Assert.AreEqual(1, propertyChangedCountIsDirty);
Assert.AreEqual(1, propertyChangedCountLocalDatabaseFilePath);
Assert.AreSame(sutViewModel, objIsDirty);
Assert.AreSame(sutViewModel, objLocalDatabaseFilePath);
}
自己解决了。
添加如下界面后:
public interface IPropertyChangedEventHandler
{
void PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e);
}
使用 Moq 的测试如下所示:
[Test]
[Category("Fast Tests")]
[Category("PropertyChanged Events")]
public void FactoryResetCommand_AllPropertiesChangedInViewModel_PropertyChangedEventsFiredAsExpected()
{
// Arrange
string originalLocalDatabaseFilePath = "oldfp";
string otherLocalDatabaseFilePath = "other" + originalLocalDatabaseFilePath;
Mock<IPropertyChangedEventHandler> mockPropertyChangedEventHandler = new Mock<IPropertyChangedEventHandler>();
Mock<IDataStoreSettingsDataModel> stubDataModel = new Mock<IDataStoreSettingsDataModel>();
stubDataModel.Setup(x => x.LocalDatabaseFilePath).Returns(originalLocalDatabaseFilePath);
IDataStoreSettingsViewModel sutViewModel = new DataStoreSettingsViewModel(
stubDataModel.Object,
ReportExceptionAsync);
sutViewModel.LocalDatabaseFilePath = otherLocalDatabaseFilePath;
sutViewModel.IsDirty = false;
sutViewModel.PropertyChanged += mockPropertyChangedEventHandler.Object.PropertyChanged;
// Act
if (sutViewModel.FactoryResetCommand.CanExecute(null))
sutViewModel.FactoryResetCommand.Execute(null);
// Assert
mockPropertyChangedEventHandler.Verify(x => x.PropertyChanged(sutViewModel,
It.Is<System.ComponentModel.PropertyChangedEventArgs>(e => e.PropertyName == nameof(DataStoreSettingsViewModel.IsDirty))),
Times.Once);
mockPropertyChangedEventHandler.Verify(x => x.PropertyChanged(sutViewModel,
It.Is<System.ComponentModel.PropertyChangedEventArgs>(e => e.PropertyName == nameof(DataStoreSettingsViewModel.LocalDatabaseFilePath))),
Times.Once);
}