创建Action的单元测试。一起运行所有测试会在某些测试方法中出现Error

时间:2014-10-13 06:42:24

标签: asp.net-mvc unit-testing

我的观点

@using (Html.BeginForm()) 
{
@Html.AntiForgeryToken()

<div class="form-horizontal">
    <div class="form-group">
        @Html.Label("Category", "Category", new { @class = "control-label col-md-2" })
        <div class="col-md-10">

            @Html.DropDownListFor(x => x.FaqCategoryId, new SelectList(ViewBag.FaqCategories, "Value", "Text"), "--Select--", new { @class = "form-control", autofocus = "autofocus", @style = "display:inline-block;" })
            <a href="#" class="tooltip-info" data-content="Category of FAQ." onmouseover="FieldDetailsOver(this);" onmouseout="    FieldDetailsOut(this);"><img src="~/Content/info.png" width="20" height="20" class="form-info" /></a>

            @Html.ValidationMessageFor(model => model.FaqCategoryId)
        </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.Title, new { @class = "control-label col-md-2" })
            <div class="col-md-10">

                @Html.TextBoxFor(model => model.Title, new { @class = "form-control", autofocus = "autofocus", @style = "display:inline-block;" })
                <a href="#" class="tooltip-info" data-content="Represents the title of the set of FAQs." onmouseover="FieldDetailsOver(this);" onmouseout="    FieldDetailsOut(this);"><img src="~/Content/info.png" width="20" height="20" class="form-info" /></a>

                @Html.ValidationMessageFor(model => model.Title)
            </div>
        </div>
    </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default sbmt-btn"/>
                <input type="reset" value="Reset" class="btn btn-default" />
            </div>
        </div>
    </div>
}

我的控制器:

public ActionResult Create(ViewModel vm)
{
    if(ModelState.IsValid)
    {
        Model modelToCreate = new Model();
        modelToCreate.Name = vm.Name;
        modelToCreate.Address = vm.Address;
        lond Id = _createService.Create(modelToCreate);
        return RedirectToAction("Index");//If everything goes fine then user is redirected to "Index" View
    }
    return View(vm);//If model state is not valid user is sent to the create view
}

这是我的测试方法,它为Create Action提供了所有有效数据。当我单独运行此测试时,它会成功传递。但是当我用其他测试方法运行此测试时,它会给出错误

我的单位测试:

public void createUnitTest()
{
    //Arrange
    var vm = new vm()
    {    
       Name = "xyz",
       Address = "mno street,Bikaner,Rajasthan"
       CategoryId = 1 //I am having a table "category" in my Dummy Database "TestDb" which contains this property
    }           

    //Act
    var result = controller.Create(vm) as RedirectToRouteResult;

    //Assert
    Assert.AreEqual("Index",result.RouteValues["action"]);
}

这是错误:

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: The INSERT statement conflicted with the FOREIGN KEY constraint  "FK_dbo.FaqSet_dbo.FaqCategory_FaqCategoryId". The conflict occurred in database "TestFaq", table  "dbo.FaqCategory", column 'Id'.
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.SqlDataReader.TryConsumeMetaData()
 at System.Data.SqlClient.SqlDataReader.get_MetaData()
 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.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
 at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
 at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
 at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
 at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.<Reader>b__c(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.Reader(DbCommand command, DbCommandInterceptionContext interceptionContext)
 at System.Data.Entity.Internal.InterceptableDbCommand.ExecuteDbDataReader(CommandBehavior behavior)
 at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
 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()
 at FAQ.Repository.UnitOfWork.Commit() in d:\project\FAQ.Repository\Common\UnitOfWork.cs:line 49
 at FAQ.Service.EntityService`1.Create(T entity) in d:\project\FAQ.Service\Common\EntityService.cs:line 35
 at FAQ.Web.Controllers.FaqSetController.Create(FaqSetModel faqset) in d:\project\FAQ.Web\Controllers\FaqSetController.cs:line 179
 at FAQ.Web.Tests.Controllers.FaqSetControllerTest.Faq_Set_Create_Test_For_All_Valid_Inputs_With_No_Blank_Field() in d:\project\FAQ.Web.Tests\Controllers\FaqSetControllerTest .cs:line 1260

1 个答案:

答案 0 :(得分:0)

问题在于,您没有正确分离的依赖项,以便您可以单独测试代码的各个部分。

即使您只测试控制器功能,行lond Id = _createService.Create(modelToCreate);也会在数据层中出现错误。

我处理这个问题的方法是使用依赖注入和模拟框架。

您可以通过通过接口属性访问所有依赖项来实现依赖项注入。例如在你的Controller类中:

ICreateService _createService { get; set; }

然后你可以创建一个友好的&#39;没有做任何事情的ICreateService实现,即:

public class FriendlyCreateService : ICreateService
{
    public void Create(Model modelToCreate)
    {
        // do nothing
    }
}

然后,当您在测试中创建控制器时,您需要注入&#34;友好的CreateService:

public void createUnitTest()
{
    //Arrange
    var vm = new vm()
    {    
       Name = "xyz",
       Address = "mno street,Bikaner,Rajasthan",
       CategoryId = 1
    };          

    // Inject friendly service
    controller._createService = new FriendlyCreateService();

    //Act
    var result = controller.Create(vm) as RedirectToRouteResult;

    //Assert
    Assert.AreEqual("Index",result.RouteValues["action"]);
}

现在可以测试您的控制器而无需担心数据层。

另一个选择是使用模拟框架,它将在运行时为您创建友好的实现。看看RhinoMocks。你的测试看起来像这样:

public void createUnitTest()
{
    // create a controller with a mocked ICreateService
    var mocks= new Mockrepository();
    Controller controller = new Controller
    {
        _createService = mocks.DynamicMock<ICreateService>();
    } 

    //Arrange
    var vm = new vm()
    {    
       Name = "xyz",
       Address = "mno street,Bikaner,Rajasthan",
       CategoryId = 1 
    }           

    //Act
    var result = controller.Create(vm) as RedirectToRouteResult;

    //Assert
    Assert.AreEqual("Index",result.RouteValues["action"]);
}