使用 MSpec 进行 ASP.NET MVC 测试。这是正确的还是需要重构?
我是 MSpec 的新手,想知道我为 ASP.NET MVC 编写测试的方式是否正确。测试通过了,但我不太喜欢它的编写方式,而且看起来很尴尬。我当然错过了一些东西。
public class AccountControllerTests3
{
protected static AccountController controller;
static IFormsAuthenticationService formsService;
static IMembershipService membershipService;
protected static ActionResult result;
protected static LogOnModel model;
Establish context = () =>
{
var controllerBuilder = new TestControllerBuilder();
formsService = MockRepository.GenerateStub<IFormsAuthenticationService>();
membershipService = MockRepository.GenerateStub<IMembershipService>();
model = MockRepository.GenerateStub<LogOnModel>();
controller =
controllerBuilder.CreateController<AccountController>(new object[]
{
formsService,
membershipService
});
};
Because user_logs = () =>
{
bool rememberMe = false;
membershipService.Stub(
x => x.ValidateUser("bdd", "mspec")).Return(true);
formsService.Stub(x => x.SignIn("bdd", rememberMe));
controller.ModelState.IsValid.ShouldBeTrue();
};
}
[Subject(typeof(AccountController), "LogInTests")]
public class When_logging_into_application_with_good_login_and_password : AccountControllerTests3
{
private It user_should_be_redirected_to_the_home_page = () =>
{
model.UserName = "bdd";
model.Password = "mspec";
result = controller.LogOn(model, string.Empty);
result.AssertActionRedirect().ToAction<HomeController>(
x => x.Index());
};
}
[Subject(typeof(AccountController), "LogInTests")]
public class When_logging_into_application_with_bad_login_and_password : AccountControllerTests3
{
It The_error_message_should_be_shown = () =>
{
model.UserName = "BAD";
model.Password = "BAD";
result = controller.LogOn(model, string.Empty);
controller.ModelState[""].Errors[0].ErrorMessage.ShouldEqual(
"The user name or password provided is incorrect.");
};
}
提前致谢,
托马斯
I'm new to MSpec and would like to know if the way I wrote my test for ASP.NET MVC is correct. The test passes but I don't really like the way it's written and it seems awkward. I'm certainly missing something.
public class AccountControllerTests3
{
protected static AccountController controller;
static IFormsAuthenticationService formsService;
static IMembershipService membershipService;
protected static ActionResult result;
protected static LogOnModel model;
Establish context = () =>
{
var controllerBuilder = new TestControllerBuilder();
formsService = MockRepository.GenerateStub<IFormsAuthenticationService>();
membershipService = MockRepository.GenerateStub<IMembershipService>();
model = MockRepository.GenerateStub<LogOnModel>();
controller =
controllerBuilder.CreateController<AccountController>(new object[]
{
formsService,
membershipService
});
};
Because user_logs = () =>
{
bool rememberMe = false;
membershipService.Stub(
x => x.ValidateUser("bdd", "mspec")).Return(true);
formsService.Stub(x => x.SignIn("bdd", rememberMe));
controller.ModelState.IsValid.ShouldBeTrue();
};
}
[Subject(typeof(AccountController), "LogInTests")]
public class When_logging_into_application_with_good_login_and_password : AccountControllerTests3
{
private It user_should_be_redirected_to_the_home_page = () =>
{
model.UserName = "bdd";
model.Password = "mspec";
result = controller.LogOn(model, string.Empty);
result.AssertActionRedirect().ToAction<HomeController>(
x => x.Index());
};
}
[Subject(typeof(AccountController), "LogInTests")]
public class When_logging_into_application_with_bad_login_and_password : AccountControllerTests3
{
It The_error_message_should_be_shown = () =>
{
model.UserName = "BAD";
model.Password = "BAD";
result = controller.LogOn(model, string.Empty);
controller.ModelState[""].Errors[0].ErrorMessage.ShouldEqual(
"The user name or password provided is incorrect.");
};
}
Thanks in advance,
Thomas
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
当我使用 MSpec 编写测试时,我的目标之一是将“Assert”或“It”缩减到一行。 MSpec 与 NUnit 不同,它只执行一次 Context(由当前类和所有基类的 Estlave 子句以及because 子句组成),然后执行所有规范(It 子句)。
这样做的目的是明确地强制您不要执行 It 子句中的任何行为,而是观察它。
您实际上在这里所做的是像 NUnit 一样使用 MSpec。尝试在单个类中重写测试(不使用继承)。向后工作...在 It 中,放置一行来断言您想要断言的内容。也许是 AssertRedirect。在“原因”中,尝试放置一行使得观察结果可观察。这可能是您对控制器登录方法的调用。最后,在“建立上下文”中,您需要放置其他所有内容。
一段时间后,您可能只想将建立上下文中的一些内容提取到基类中,但这样做时,请确保您的整个子类在理解方面是独立的。读者不需要阅读基类来了解实际规范的作用。隐藏仪式实现细节是可以的,但一定要将它们隐藏在描述性方法名称后面。
还有另一行我不确定:
controller.ModelState.IsValid.ShouldBeTrue();
如果这是一个测试,它可能应该在它自己的 It 子句中。虽然真的,你想测试一下吗?你在这里测试什么?您的控制器不应该根据模型是否有效来采取行动吗?该操作的结果不应该是可观察的(验证错误而不是登录错误)。我只是想知道您是否真的需要测试这个。
还有其他一些事情需要检查,首先是使用 R# 进行样式设置,看来您的测试正在成为 R# 默认设置的受害者。我在这里发布了如何解决这个问题:
http://codebetter.com/blogs/aaron.jensen/archive/2008/10/19/getting-resharper-and-vs-to-play-nice-with -mspec.aspx
另外,James Broome 有一些不错的 MVC MSpec 扩展值得一试:
http ://jamesbroo.me/introducing-machinespecationsmvc/
祝你好运并享受!如果您有任何其他不相关的问题,请随时在 Twitter 上联系我。
One of my goals when I write tests with MSpec is to get the "Assert" or the "It" down to one line. MSpec is not like NUnit in that it only executes the Context (made up of the Establish clauses from the current class and all base classes and the Because clause) once followed by all of the Specifications (It clauses).
This is designed explicitly to force you to not perform any behavior in the It clauses, but rather observe it.
What you're actually doing here is using MSpec like NUnit. Try and rewrite the tests in a single class (using no inheritance). Work backwards... in the It, place a single line that asserts what you want to assert. Perhaps the AssertRedirect. In the Because, try and put a single line that causes the observations to be observable. This would probably be your call to the controller's logon method. Finally, in the "Establish context" you'd want to put everything else.
After a while, you may want to pull some of the things in the Establish context only into a base class, but in doing so, be sure that your entire subclass stands alone in terms of understanding. A reader shouldn't need to read the base class in order to understand what the actual spec is doing. It's ok to hide ceremonial implementation details, but be sure to hide them behind descriptive method names.
There's another line I'm not sure about:
controller.ModelState.IsValid.ShouldBeTrue();
If this is a test, it should probably be in its own It clause. Though really, do you want to test this? What are you testing here? Shouldn't your controller take an action based on whether or not the model is valid? Shouldn't the result of that action be observable (validation error instead of login error). I just wonder if you really need to test this.
A few other things to check out, first for styling with R#, it seems your tests are falling victim to R#'s defaults. I posted about how to fight this here:
http://codebetter.com/blogs/aaron.jensen/archive/2008/10/19/getting-resharper-and-vs-to-play-nice-with-mspec.aspx
Also, James Broome has some nice MVC MSpec extensions that are worth checking out:
http://jamesbroo.me/introducing-machinespecificationsmvc/
Good luck and Enjoy! Feel free to ping me on twitter if you have any other unrelated questions.
这里有一个注释:不要使用
CreateController
方法,而是使用InitializeController
方法,因为它编译时更安全并且重构更友好。而不是:
执行:
如果更改控制器构造函数参数,第一个仍然会编译,并且它将在运行时崩溃,而第二个将生成编译时错误。
Here's a remark: instead of using
CreateController
method useInitializeController
, because it is compile-time safer and refactor friendlier.Instead of:
Do:
The first will still compile if you change the controller constructor arguments and it will blow at runtime, while the second will generate a compile-time error.