首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在不同的体系结构层使用NSpec

在不同的体系结构层使用NSpec
EN

Software Engineering用户
提问于 2012-12-09 07:37:43
回答 1查看 529关注 0票数 4

在阅读了nspec.org的快速入门之后,我意识到NSpec可能是一个有用的工具,在一个只使用NUnit变得有点麻烦的场景中。

我给网站添加了一个OAuth (或,DotNetOpenAuth),很快就把编写测试方法弄得一团糟,比如

代码语言:javascript
复制
[Test]
public void UserIsLoggedInLocallyPriorToInvokingExternalLoginAndExternalLoginSucceedsAndExternalProviderIdIsNotAlreadyAssociatedWithUserAccount()
{
...
}

..。最后,我得到了这个主题的十几个排列,因为用户已经在本地登录而不是本地登录,外部登录成功或失败等等。不仅方法名称难以处理,而且每个测试都需要包含与其他测试不同集合相同部分的设置。

我意识到NSpec的增量设置功能会在这方面发挥很大的作用,有一段时间我用类似于

代码语言:javascript
复制
  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层编写测试代码之前,这种方法似乎非常有效,我将控制器的视图模型操作委托给它,并协调较低的数据存储层的操作:

代码语言:javascript
复制
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回答“您的问题无效”。

EN

回答 1

Software Engineering用户

发布于 2013-06-09 02:35:38

当心:我从未真正使用过NSpec,但我一直使用它的表亲RSpec。正是我使用这个工具的经验让我找到了下面的答案。

我建议使用这个命名约定:

代码语言:javascript
复制
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中,这类似于:

代码语言:javascript
复制
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
end

RSpec还提供了其他构造,所以我可能不会像上面那样编写我的规范,但是我可以理解我遇到的其他人的规范。有一个很棒的RSpec网站叫做更好的规格,它可以给你更多的灵感。当然,你必须翻译一些语法。

票数 1
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/178804

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档