我已经编辑并简化了这个问题。
如果我的HomeController上有这个方法:
public ActionResult Strangeness( int id )
{
StrangenessClass strangeness = null;
if( id == 1 )
{
strangeness = new StrangenessClass() { Name="Strangeness", Desc="Really weird behavior" };
}
return View( strangeness );
}
有这个课程:
public class StrangenessClass
{
public string Name { get; set; }
public string Desc { get; set; }
}
为什么这个单元测试失败了?
[TestMethod]
public void Strangeness()
{
HomeController controller = new HomeController();
ViewResult result = controller.Strangeness( 1 ) as ViewResult;
var model = result.ViewData.Model;
result = controller.Strangeness( 2 ) as ViewResult;
model = result.ViewData.Model;
Assert.IsNull( model );
}
我理解通常情况下,我会测试一个测试null条件的测试,另一个测试一个良好的条件,但我在测试我的删除控制器时遇到了这个问题。在删除测试中,我通常会获取记录,删除记录,然后再次尝试获取它。我第二次获取它时应该为null,但事实并非如此。所以,我如上所述将问题解决了。
如果这不是测试删除的正确方法,你会怎么做?你不需要确保记录真的被删除了吗?
答案 0 :(得分:1)
您不应该重复使用控制器来处理多个请求,这正是您在此处所做的。
无论如何,如果你检查source code for MVC,你会发现这种行为的原因:
protected internal virtual ViewResult View(string viewName, string masterName, object model)
{
if (model != null)
{
base.ViewData.Model = model;
}
return new ViewResult { ViewName = viewName, MasterName = masterName, ViewData = base.ViewData, TempData = base.TempData };
}
如果模型为null,则不会将其分配给ViewData.Model属性。
如果您想要正确的行为,请为第二次调用HomeController.Strangeness
创建一个新的控制器。
答案 1 :(得分:0)
目前尚不清楚您在测试什么。在测试方法的“编配”部分中,您将调用第一个“删除”操作,而在“法案”部分中,您将调用第二个。所以你在测试控制器吗?如果是,那么为什么要调用编配部分中的第一个删除方法?
_stateService
变量是什么?它是一个接口还是您实际在单元/集成测试中删除数据库中的记录?
所以我建议你编写多个测试,每个测试验证测试对象的精确行为,我认为是控制器。因此,您应该为要测试的不同删除操作分离单元测试。
假设_stateService
是一个可以模拟的界面,我建议你设计你的控制器,你的测试看起来像这样(使用Rhino Mocks和MVCContrib.TestHelper):
[TestClass]
public class DevisControllerTests : TestControllerBuilder
{
private HomeController _sut; // Subject Under Test
private IStateService _stateServiceStub; // Dependency of the SUT
[TestInitialize()]
public void MyTestInitialize()
{
_stateServiceStub = MockRepository.GenerateStub<IStateService>();
_sut = new HomeController(_stateServiceStub);
InitializeController(_sut); // this method comes from the base class TestControllerBuilder
}
[TestMethod]
public void HomeController_Delete_Action_Should_Fetch_State_From_Db_And_Pass_It_To_The_View()
{
// arrange
var id = 4;
var expectedState = new State();
_stateServiceStub.Stub(x => x.GetById(id)).Return(expectedState);
// act
var actual = _sut.Delete(id);
// assert
actual
.AssertViewRendered()
.WithViewData<State>()
.ShouldBe(expectedState);
}
[TestMethod]
public void HomeController_Delete_Action_Handler_Should_Return_Default_View_If_Model_Null()
{
// act
var actual = _sut.Delete(null);
// assert
actual.AssertViewRendered();
}
[TestMethod]
public void HomeController_Delete_Action_Handler_Should_Return_View_If_Exception_Thrown_From_Service()
{
// arrange
var model = new State();
_stateServiceStub.Stub(x => x.Delete(model)).Throw(new Exception("oops"));
// act
var actual = _sut.Delete(state);
// assert
actual
.AssertViewRendered()
.WithViewData<State>()
.ShouldBe(model);
}
[TestMethod]
public void HomeController_Delete_Action_Handler_Should_Redirect_If_Model_Successfully_Deleted()
{
// arrange
var model = new State();
// act
var actual = _sut.Delete(state);
// assert
actual
.AssertActionRedirect()
.ToAction<HomeController>(c => c.Index());
_stateServiceStub.AssertWasCalled(x => x.Delete(model));
}
}