DRY-ING非常类似的规格与MSpec(BDD指南)ASP.NET MVC控制器动作 [英] DRY-ing very similar specs for ASP.NET MVC controller action with MSpec (BDD guidelines)

查看:140
本文介绍了DRY-ING非常类似的规格与MSpec(BDD指南)ASP.NET MVC控制器动作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个非常相似的控制器动作两个非常类似的规格:VoteUp(INT ID)和VoteDown(INT ID)。这些方法允许用户投票后向上或向下;有点像表决向上/向下功能的计算器的问题。规格是:

I have two very similar specs for two very similar controller actions: VoteUp(int id) and VoteDown(int id). These methods allow a user to vote a post up or down; kinda like the vote up/down functionality for StackOverflow questions. The specs are:

VoteDown:

[Subject(typeof(SomeController))]
public class When_user_clicks_the_vote_down_button_on_a_post : SomeControllerContext
{
    Establish context = () =>
    {
        post = PostFakes.VanillaPost();
        post.Votes = 10;

        session.Setup(s => s.Single(Moq.It.IsAny<Expression<Func<Post, bool>>>())).Returns(post);
        session.Setup(s => s.CommitChanges());
    };

    Because of = () => result = controller.VoteDown(1);

    It should_decrement_the_votes_of_the_post_by_1 = () => suggestion.Votes.ShouldEqual(9);
    It should_not_let_the_user_vote_more_than_once;
}



VoteUp:

VoteUp:

[Subject(typeof(SomeController))]
public class When_user_clicks_the_vote_down_button_on_a_post : SomeControllerContext
{
    Establish context = () =>
    {
        post = PostFakes.VanillaPost();
        post.Votes = 0;

        session.Setup(s => s.Single(Moq.It.IsAny<Expression<Func<Post, bool>>>())).Returns(post);
        session.Setup(s => s.CommitChanges());
    };

    Because of = () => result = controller.VoteUp(1);

    It should_increment_the_votes_of_the_post_by_1 = () => suggestion.Votes.ShouldEqual(1);
    It should_not_let_the_user_vote_more_than_once;
}



所以,我有两个问题:

So I have two questions:


  1. 我应该如何去DRY-ING这两个规格?它甚至建议,或者我应该确实有每个控制器动作一规范呢?我知道我通常应,但这种感觉就像在重复自己了不少。

  1. How should I go about DRY-ing these two specs? Is it even advisable or should I actually have one spec per controller action? I know I Normally should, but this feels like repeating myself a lot.

有什么办法实施第二同规格之内?请注意,这should_not_let_the_user_vote_more_than_once; 要求我的规格叫 controller.VoteDown(1)的两倍。我知道最简单的将是为它创建一个单独的规范太多,但它会被复制和粘贴相同的代码的再次 ...

Is there any way to implement the second It within the same spec? Note that the It should_not_let_the_user_vote_more_than_once; requires me the spec to call controller.VoteDown(1) twice. I know the easiest would be to create a separate spec for it too, but it'd be copying and pasting the same code yet again...

我仍然得到BDD(和MSpec)很多时候并不清楚我应该往哪个方向走,或挂起什么是最好的做法或准则BDD是。 。任何帮助,将不胜感激。

I'm still getting the hang of BDD (and MSpec) and many times it is not clear which way I should go, or what the best practices or guidelines for BDD are. Any help would be appreciated.

推荐答案

我将与你的第二个问题开始:有一个在MSpec一项功能,将与帮助在的重复它字段,但在这种情况下我建议不要使用它。该功能称为行为和是这样的:

I'll start with your second question: There is a feature in MSpec that would help with the duplication of the It fields, but in this scenario I would advise against using it. The feature is called Behaviors and goes something like this:

[Subject(typeof(SomeController))]
public class When_user_clicks_the_vote_up_button_on_a_post : SomeControllerContext
{
    // Establish and Because cut for brevity.

    It should_increment_the_votes_of_the_post_by_1 =
        () => suggestion.Votes.ShouldEqual(1);

    Behaves_like<SingleVotingBehavior> a_single_vote;
}

[Subject(typeof(SomeController))]
public class When_user_clicks_the_vote_down_button_on_a_post : SomeControllerContext
{
    // Establish and Because cut for brevity.

    It should_decrement_the_votes_of_the_post_by_1 = 
        () => suggestion.Votes.ShouldEqual(9);

    Behaves_like<SingleVotingBehavior> a_single_vote;
}

[Behaviors]
public class SingleVotingBehavior
{
    It should_not_let_the_user_vote_more_than_once =
        () => true.ShouldBeTrue();
}



要在行为类主张上的任何字段需要为受保护的静态中的行为和上下文类两种。该MSpec源代码包含另一个例子

Any fields you want to assert on in the behavior class need to be protected static in both the behavior and the context class. The MSpec source code contains another example.

我建议不要使用行为,因为你的榜样其实包含四个上下文。当我想你想用的业务含义条款的代码要表达什么,四种不同的情况下出现的:

I advise against using behaviors because your example actually contains four contexts. When I think about what you're trying to express with the code in terms of "business meaning", four different cases emerge:


  • 用户首次

  • 票了
  • 用户投票下来,第一次

  • 用户投票了第二次

  • 用户投票下来,第二次

  • User votes up for the first time
  • User votes down for the first time
  • User votes up for the second time
  • User votes down for the second time

对于每个四个不同的场景我会创建一个单独的背景下,紧密地介绍了如何该系统应该表现。四上下文类有很多重复的代码,这给我们带来的第一个问题。

For each of the four different scenarios I would create a separate context that closely describes how the system should behave. Four context classes are a lot of duplicate code, which brings us to your first question.

在模板下面有一个基类具有描述性的名称的方法当你叫他们会发生什么。因此,而不是依赖于因为的事实,MSpec会叫继承字段中自动完成,你把什么是向右的建立上下文的重要信息。从我的经验,这将在以后帮助你很多,当你读一个规范的情况下,它是失败的。取而代之的导航类层次结构,你立即获得该发生的安装的感觉。

In the "template" below there is one base class with methods that have descriptive names of what will happen when you call them. So instead of relying on the fact that MSpec will call "inherited" Because fields automatically, you put information on what's important to the context right in the Establish. From my experience this will help you a lot later when you read a spec in case it is failing. Instead of navigating a class hierarchy you immediately get a feeling for the setup that takes place.

在一个相关的说明,第二个好处是,你只需要一个基类,无论有多少不同的情况下与您获得特定设置。

On a related note, the second advantage is that you only need one base class, no matter how many different contexts with specific setup you derive.

public abstract class VotingSpecs
{
    protected static Post CreatePostWithNumberOfVotes(int votes)
    {
        var post = PostFakes.VanillaPost();
        post.Votes = votes;
        return post;
    }

    protected static Controller CreateVotingController()
    {
        // ...
    }

    protected static void TheCurrentUserVotedUpFor(Post post)
    {
        // ...
    }
}

[Subject(typeof(SomeController), "upvoting")]
public class When_a_user_clicks_the_vote_up_button_on_a_post : VotingSpecs
{
    static Post Post;
    static Controller Controller;
    static Result Result ;

    Establish context = () =>
    {
        Post = CreatePostWithNumberOfVotes(0);

        Controller = CreateVotingController();
    };

    Because of = () => { Result = Controller.VoteUp(1); };

    It should_increment_the_votes_of_the_post_by_1 =
        () => Post.Votes.ShouldEqual(1);
}


[Subject(typeof(SomeController), "upvoting")]
public class When_a_user_repeatedly_clicks_the_vote_up_button_on_a_post : VotingSpecs
{
    static Post Post;
    static Controller Controller;
    static Result Result ;

    Establish context = () =>
    {
        Post = CreatePostWithNumberOfVotes(1);
        TheCurrentUserVotedUpFor(Post);

        Controller = CreateVotingController();
    };

    Because of = () => { Result = Controller.VoteUp(1); };

    It should_not_increment_the_votes_of_the_post_by_1 =
        () => Post.Votes.ShouldEqual(1);
}

// Repeat for VoteDown().

这篇关于DRY-ING非常类似的规格与MSpec(BDD指南)ASP.NET MVC控制器动作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆