在阅读了nspec.org的快速入门之后,我意识到NSpec可能是一个有用的工具,在一个只使用NUnit变得有点麻烦的场景中。
我给网站添加了一个OAuth (或,DotNetOpenAuth),很快就把编写测试方法弄得一团糟,比如
[Test]
public void UserIsLoggedInLocallyPriorToInvokingExternalLoginAndExternalLoginSucceedsAndExternalProviderIdIsNotAlreadyAssociatedWithUserAccount()
{
...
}..。最后,我得到了这个主题的十几个排列,因为用户已经在本地登录而不是本地登录,外部登录成功或失败等等。不仅方法名称难以处理,而且每个测试都需要包含与其他测试不同集合相同部分的设置。
我意识到NSpec的增量设置功能会在这方面发挥很大的作用,有一段时间我用类似于
act = () => { actionResult = controller.ExternalLoginCallback(returnUrl); };
context["The user is already logged in"] = () =>
{
before = () => identity.Setup(x => x.IsAuthenticated).Returns(true);
context["The external login succeeds"] = () =>
{
before = () => oauth.Setup(x => x.VerifyAuthentication(It.IsAny<string>())).Returns(new AuthenticationResult(true, providerName, "provideruserid", "username", new Dictionary<string, string>()));
context["External login already exists for current user"] = () =>
{
before = () => authService.Setup(x => x.ExternalLoginExistsForUser(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Returns(true);
it["Should add 'login sucessful' alert"] = () =>
{
var alerts = (IList<Alert>)controller.TempData[TempDataKeys.AlertCollection];
alerts[0].Message.should_be_same("Login successful");
alerts[0].AlertType.should_be(AlertType.Success);
};
it["Should return a redirect result"] = () => actionResult.should_cast_to<RedirectToRouteResult>();
};
context["External login already exists for another user"] = () =>
{
before = () => authService.Setup(x => x.ExternalLoginExistsForAnyOtherUser(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Returns(true);
it["Adds an error alert"] = () =>
{
var alerts = (IList<Alert>)controller.TempData[TempDataKeys.AlertCollection];
alerts[0].Message.should_be_same("The external login you requested is already associated with a different user account");
alerts[0].AlertType.should_be(AlertType.Error);
};
it["Should return a redirect result"] = () => actionResult.should_cast_to<RedirectToRouteResult>();
};在我准备为我的ApplicationServices层编写测试代码之前,这种方法似乎非常有效,我将控制器的视图模型操作委托给它,并协调较低的数据存储层的操作:
public void CreateUserAccountFromExternalLogin(RegisterExternalLoginModel model)
{
throw new NotImplementedException();
}
public void AssociateExternalLoginWithUser(string userName, string provider, string providerUserId)
{
throw new NotImplementedException();
}
public string GetLocalUserName(string provider, string providerUserId)
{
throw new NotImplementedException();
}我不知道在世界上如何命名测试类、测试方法,甚至不知道我是否应该从上面的大型代码片段中将这一层的测试包含到测试类中,这样就可以不考虑体系结构分层来测试单个特性或用户操作。
我找不到任何的教程或博客文章,涵盖更多的简单例子,所以我会感谢任何建议或指向正确的方向。只要提供了一些解释,我甚至欢迎-type回答“您的问题无效”。
发布于 2013-06-09 02:35:38
当心:我从未真正使用过NSpec,但我一直使用它的表亲RSpec。正是我使用这个工具的经验让我找到了下面的答案。
我建议使用这个命名约定:
class describe_your_class : nspec
{
void instance_method_CreateUserAccountFromExternalLogin()
{
before = () =>
{
account_repository = new AccountRepository();
fixture = new YourClass(account_repository);
}
context["with a valid external login model"] = () =>
{
before = () =>
{
account = CreateValidAccountForTesting();
fixture.CreateUserAccountFromExternalLogin(account);
}
it["creates a user account"] = () => account_repository.count.should_be(1);
}
context["with a null external login model"] = () =>
{
before = () =>
{
fixture.CreateUserAccountFromExternalLogin(null);
}
it["silently does not create a user"] = () => account_repository.count.should_be(0);
}
}
}在RSpec中,这类似于:
describe YourClass do
# "#method_name" is a convention that's used in Ruby docs to refer to an
# instance method. ".method_name" would refer to a class method
describe '#create_user_account_from_external_login' do
before do
# the "@" here indicates that these are instance variables
@account_repository = AccountRepository.new
@fixture = YourClass.new(@account_repository)
end
context 'with a valid external login' do
before do
account = create_valid_account_for_testing()
@fixture.create_user_account_from_external_login(account)
end
it 'creates a user account' do
@account_repository.count.should == 1
end
end
context 'with a nil external login' do
before do
@fixture.create_user_account_from_external_login(nil)
end
it 'silently does not create a user account' do
@account_repository.count.should == 0
end
end
end
endRSpec还提供了其他构造,所以我可能不会像上面那样编写我的规范,但是我可以理解我遇到的其他人的规范。有一个很棒的RSpec网站叫做更好的规格,它可以给你更多的灵感。当然,你必须翻译一些语法。
https://softwareengineering.stackexchange.com/questions/178804
复制相似问题