我需要帮助测试以下代码
public virtual void Update(T entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
int iretries = 0;
bool success = false;
do
{
try
{
this.context.SaveChanges();
success = true;
}
catch (DbUpdateConcurrencyException ex)
{
// Get the current entity values and the values in the database
// as instances of the entity type
var entry = ex.Entries.Single();
var databaseValues = entry.GetDatabaseValues();
// Choose an initial set of resolved values. In this case we
// make the default be the values currently in the database: StoreWins
object resolvedValues = ResolveConcurrency(databaseValues.ToObject());
// Update the original values with the database values and
// the current values with whatever the user choose.
entry.OriginalValues.SetValues(databaseValues);
entry.CurrentValues.SetValues(resolvedValues);
// give up after n retries
if (++iretries == NUMBER_OF_CONC_RETRIES)
throw;
}
catch (Exception)
{
//rethrow
throw;
}
} while (!success);
}
我想对DbUpdateConcurrencyException
分支进行单元测试。
因此,一个简单的测试场景是:
DbUpdateConcurrencyException
SaveChanges
以抛出上述异常SaveChanges
被称为NUMBER_OF_CONC_RETRIES
Update
方法重新抛出异常在当前状态下,无法测试上述测试场景,我无法模拟异常以包含IEnumerable<DbEntityEntry>
个DbEntityEntry
;我不能嘲笑GetDatabaseValues()
等等。
一个简单的解决方案是插入一个新的抽象层;假设使用一个接口来抽象当前位于catch块中的整个代码,并提供一个什么都不做的模拟。
但是当我想要测试该接口的实现时,我最终会遇到这种情况,最终会遇到与上面相同的问题。如何模仿DbUpdateConcurrencyException
,GetDatabaseValues
等
我正在使用moq进行嘲弄。
感谢您的输入
答案 0 :(得分:1)
如果你不能嘲笑某些东西,你必须将它隐藏在其他东西之后,你可以在测试中模拟或覆盖。您的测试实际上不需要使用所有内容来加载值并在条目中设置它们 - 这完全依赖于EF,并且在模拟上下文时您不会测试它,因为这意味着重新实现EF的逻辑{ {1}}。您所需要做的就是:
SaveChanges
其中catch (DbUpdateConcurrencyException ex) {
RefreshValues(ex);
// give up after n retries
if (++iretries == NUMBER_OF_CONC_RETRIES)
throw;
}
可以是RefreshValues
方法,您可以通过提供类的测试版本(甚至可以使用Moq实现此目的)或通过继承此类的测试来覆盖测试并直接在测试类中重写该方法。
要设置Moq,您需要为您的上下文显示SaveChanges方法的接口:
protected virtual
如果您需要测试它是否适用于少数抛出,您需要在测试中保持计数器并在回调中使用它来决定是否抛出。