测试 EF 保存更改修饰符。传入 DbPropertyValues
Testing EF Save Changes Modifiers. Passing in DbPropertyValues
尝试通过覆盖 EF SaveChanges 方法在 C# 中执行一些业务逻辑。
这个想法是对诸如此字段是否已更改更新此字段之类的事情进行一些高级计算。而且这个字段是子类减去一些其他字段的总和,高级业务垃圾你懂的。
因为它真的很复杂,我们想测试一下它的填充物。添加测试效果很好,但是我们似乎无法测试更新测试,因为我们已经编写了一个接口,其中传递了相关方法
签名看起来像这样
void Update(object entity, DbPropertyValues currentValues, DbPropertyValues originalValues);
在完整的 EF 中调用它时效果很好
public override int SaveChanges()
{
var added = ChangeTracker.Entries().Where(p => p.State == EntityState.Added).Select(p => p.Entity);
var updated = ChangeTracker.Entries().Where(p => p.State == EntityState.Modified).Select(p => p);
var context = new ChangeAndValidationContext();
foreach (var item in added)
{
var strategy = context.SelectStrategy(item);
strategy.Add(item);
}
foreach (var item in updated)
{
var strategy = context.SelectStrategy(item);
strategy.Update(item.Entity, item.CurrentValues, item.OriginalValues);
}
return base.SaveChanges();
}
我们只是想不出如何为我们的测试传递原始或更新的 DbPropertyValues。请帮助我们弄清楚如何测试该方法。
如果您有 Visual Studio 2012 Update 2+,您可以通过右键单击测试项目中的项目引用来 "Add Fakes Assembly" for EntityFramework。
添加后,您可以创建完全由您控制的 System.Data.Entity.Infrastructure.Fakes.ShimDbPropertyValues
实例。例如
var shim = new System.Data.Entity.Infrastructure.Fakes.ShimDbPropertyValues();
shim.ItemGetString = s => "Hello, World!";
当在 fake/shim DbPropertyValues
上调用 GetString
时,它将 return "Hello, World!"。
此处有更多详细信息:https://msdn.microsoft.com/en-us/library/hh549175.aspx
我决定更好的方法是改变策略的预期。而不是
void Update(object entity, DbPropertyValues currentValues, DbPropertyValues originalValues);
我让它接受了
void Update(object entity, Dictionary<string, object> currentValues, Dictionary<string, object> originalValues);
这意味着我更改了传递给更新方法的值
foreach (var item in updated)
{
var strategy = context.SelectStrategy(item);
strategy.Update(item.Entity, item.CurrentValues.ValuesToValuesDictionary(), item.OriginalValues.ValuesToValuesDictionary());
}
然后我创建了那个扩展方法
public static class DbPropertyValueExtensions
{
public static Dictionary<string, object> ValuesToValuesDictionary(this DbPropertyValues vals)
{
var retVal = new Dictionary<string, object>();
foreach (var propertyName in vals.PropertyNames)
{
if (!retVal.ContainsKey(propertyName))
{
retVal.Add(propertyName, vals[propertyName]);
}
}
return retVal;
}
}
这意味着我的测试需要通过那些词典。
[Test]
public void DateLastModifiedUpdatesOnUpdate()
{
//Arrange
var toTest = LossFactoryHelper.Create();
var lossCheckAndValidationAddStrategy = new LossChangeAndValidationStrategy();
var now = DateTime.UtcNow;
var originalValues = toTest.GetValuesNow();
//Act
toTest.mny_deductible = -1;
var currentValues = toTest.GetValuesNow();
lossCheckAndValidationAddStrategy.Update(toTest, originalValues, currentValues);
//Assert
Assert.GreaterOrEqual(toTest.clc_DateLastModified, now);
}
以及帮助获取值快照的扩展方法,不必一遍又一遍地创建字典
public static class ReflectionToGetCurrentValuesExtension
{
public static Dictionary<string, object> GetValuesNow(this object obj)
{
var retVal = new Dictionary<string, object>();
var type = obj.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.CanRead && property.CanWrite)
{
if (!retVal.ContainsKey(property.Name))
{
retVal.Add(property.Name, property.GetValue(obj));
}
}
}
return retVal;
}
}
尝试通过覆盖 EF SaveChanges 方法在 C# 中执行一些业务逻辑。
这个想法是对诸如此字段是否已更改更新此字段之类的事情进行一些高级计算。而且这个字段是子类减去一些其他字段的总和,高级业务垃圾你懂的。
因为它真的很复杂,我们想测试一下它的填充物。添加测试效果很好,但是我们似乎无法测试更新测试,因为我们已经编写了一个接口,其中传递了相关方法 签名看起来像这样
void Update(object entity, DbPropertyValues currentValues, DbPropertyValues originalValues);
在完整的 EF 中调用它时效果很好
public override int SaveChanges()
{
var added = ChangeTracker.Entries().Where(p => p.State == EntityState.Added).Select(p => p.Entity);
var updated = ChangeTracker.Entries().Where(p => p.State == EntityState.Modified).Select(p => p);
var context = new ChangeAndValidationContext();
foreach (var item in added)
{
var strategy = context.SelectStrategy(item);
strategy.Add(item);
}
foreach (var item in updated)
{
var strategy = context.SelectStrategy(item);
strategy.Update(item.Entity, item.CurrentValues, item.OriginalValues);
}
return base.SaveChanges();
}
我们只是想不出如何为我们的测试传递原始或更新的 DbPropertyValues。请帮助我们弄清楚如何测试该方法。
如果您有 Visual Studio 2012 Update 2+,您可以通过右键单击测试项目中的项目引用来 "Add Fakes Assembly" for EntityFramework。
添加后,您可以创建完全由您控制的 System.Data.Entity.Infrastructure.Fakes.ShimDbPropertyValues
实例。例如
var shim = new System.Data.Entity.Infrastructure.Fakes.ShimDbPropertyValues();
shim.ItemGetString = s => "Hello, World!";
当在 fake/shim DbPropertyValues
上调用 GetString
时,它将 return "Hello, World!"。
此处有更多详细信息:https://msdn.microsoft.com/en-us/library/hh549175.aspx
我决定更好的方法是改变策略的预期。而不是
void Update(object entity, DbPropertyValues currentValues, DbPropertyValues originalValues);
我让它接受了
void Update(object entity, Dictionary<string, object> currentValues, Dictionary<string, object> originalValues);
这意味着我更改了传递给更新方法的值
foreach (var item in updated)
{
var strategy = context.SelectStrategy(item);
strategy.Update(item.Entity, item.CurrentValues.ValuesToValuesDictionary(), item.OriginalValues.ValuesToValuesDictionary());
}
然后我创建了那个扩展方法
public static class DbPropertyValueExtensions
{
public static Dictionary<string, object> ValuesToValuesDictionary(this DbPropertyValues vals)
{
var retVal = new Dictionary<string, object>();
foreach (var propertyName in vals.PropertyNames)
{
if (!retVal.ContainsKey(propertyName))
{
retVal.Add(propertyName, vals[propertyName]);
}
}
return retVal;
}
}
这意味着我的测试需要通过那些词典。
[Test]
public void DateLastModifiedUpdatesOnUpdate()
{
//Arrange
var toTest = LossFactoryHelper.Create();
var lossCheckAndValidationAddStrategy = new LossChangeAndValidationStrategy();
var now = DateTime.UtcNow;
var originalValues = toTest.GetValuesNow();
//Act
toTest.mny_deductible = -1;
var currentValues = toTest.GetValuesNow();
lossCheckAndValidationAddStrategy.Update(toTest, originalValues, currentValues);
//Assert
Assert.GreaterOrEqual(toTest.clc_DateLastModified, now);
}
以及帮助获取值快照的扩展方法,不必一遍又一遍地创建字典
public static class ReflectionToGetCurrentValuesExtension
{
public static Dictionary<string, object> GetValuesNow(this object obj)
{
var retVal = new Dictionary<string, object>();
var type = obj.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.CanRead && property.CanWrite)
{
if (!retVal.ContainsKey(property.Name))
{
retVal.Add(property.Name, property.GetValue(obj));
}
}
}
return retVal;
}
}