MVC4单元测试:无法在对象中插入重复键

时间:2014-05-28 22:32:50

标签: c# entity-framework asp.net-mvc-4 unit-testing ef-code-first

我正在为应用程序编写单元测试,但只有在运行测试时才会出现此错误。

Test method Tests.Items.CreateItem.CreateNewItem threw exception: 
System.Data.Entity.Infrastructure.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.Entity.Core.UpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: Violation of PRIMARY KEY constraint 'PK_dbo.Items'. Cannot insert duplicate key in object 'dbo.Items'. The duplicate key value is (e13b11ae-762f-4b86-84b3-d8224fcd84c9).
The statement has been terminated.
Result StackTrace:  
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.<NonQuery>b__0(DbCommand t, DbCommandInterceptionContext`1 c)
   at System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func`3 operation, TInterceptionContext interceptionContext, Action`3 executing, Action`3 executed)
   at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.NonQuery(DbCommand command, DbCommandInterceptionContext interceptionContext)
   at System.Data.Entity.Internal.InterceptableDbCommand.ExecuteNonQuery()
   at System.Data.Entity.Core.Mapping.Update.Internal.DynamicUpdateCommand.Execute(Dictionary`2 identifierValues, List`1 generatedValues)
   at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update()
 --- End of inner exception stack trace ---
    at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update()
   at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.<Update>b__2(UpdateTranslator ut)
   at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.Update[T](T noChangesResult, Func`2 updateFunction)
   at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.Update()
   at System.Data.Entity.Core.Objects.ObjectContext.<SaveChangesToStore>b__35()
   at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesToStore(SaveOptions options, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction)
   at System.Data.Entity.Core.Objects.ObjectContext.<>c__DisplayClass2a.<SaveChangesInternal>b__27()
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions options, Boolean executeInExistingTransaction)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChanges(SaveOptions options)
   at System.Data.Entity.Internal.InternalContext.SaveChanges()
 --- End of inner exception stack trace ---
    at System.Data.Entity.Internal.InternalContext.SaveChanges()
   at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
   at System.Data.Entity.DbContext.SaveChanges()

这是我的单元测试:

[TestClass]
public class CreateItem
{
    Guid guiItemID = Guid.Parse("e13b11ae-762f-4b86-84b3-d8224fcd84c9");

    [TestMethod]
    public void CreateNewItem()
    {
        // Create element
        Item item = new Item();
        item.ID = guiItemID;

        // Save element to database
        TestContext db = new TestContext();
        db.Items.Add(item);
        db.SaveChanges();
    }

这是我模型上的ID属性:

[Key]
public Guid ID { get; set; }

如果我从种子方法运行相同的代码,则数据库将以此项目播种。所以我知道与数据库交互的代码可以正常工作。

如果我改变:

item.ID = guiItemID;

为:

item.ID = Guid.NewGuid();

然后测试通过。但我想设置ID,以便稍后通过检索相同的项目来测试其他内容。

我的项目在数据库中已不存在。这是一个空数据库。

为什么我的测试会抛出一个错误,好像该项已经存在,但显然不是这样?测试是否以某种方式运行两次?这是我列表中唯一的测试。

1 个答案:

答案 0 :(得分:0)

每次运行时UT都是一个单独的数据库,现在如果你停止测试用例,那么有时创建的数据库不会被删除。编写测试的推荐方法是进行测试清理[TestCleanup]方法并执行清理活动

  [TestCleanup]
  public void TestCleanup()
  {
       var entity = db.Item.Find(guiItemID);
       db.Item.Remove(entity);
       db.SaveChanges();
  }