我的观点
@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
答案 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"]);
}