在SpecFlow

时间:2017-12-17 17:55:25

标签: c# visual-studio specflow gherkin feature-file

我正在学习如何使用SpecFlow,并希望编写一个功能,其中包括用于注册用户的测试。

我有第一个测试用例,注册一个有效用户。 这将涉及输入用户名,电子邮件,密码和确认密码。单击“提交”,应注册用户并将其添加到数据库。

我的下一个测试用例将是一个无效的情况,我将执行上述步骤,然后尝试注册具有相同用户名的用户,这应该返回错误。 从我能从我发现的例子中可以理解,我可以使用"背景"在我的.feature文件中,指定我想在.feature文件中的每个方案之前执行的步骤。这将涉及在Background部分中创建一个步骤来注册用户并调用在Valid Register User case中创建的方法。 这个question对我有所帮助,但也为我提出了更多问题,因为我无法理解可用的标签和挂钩的正确用法。

我的问题是:

  1. 有效测试用例是否应与其他方案位于同一文件中?如果为该功能文件中的每个方案调用了Background部分,那么它将导致有效测试失败(因为它本质上是有效的测试用例)。
  2. 这是构建测试的正确方法吗?我已经阅读了可以使用[BeforeScenario] / [AfterScenario]等的SpecFlow挂钩,但我不确定何时应该在Background标签上考虑这些挂钩。
  3. 我觉得有很多可以避免的重复,例如只是将有效的测试用例作为另一个测试的一个步骤,而不是为它创建一个步骤以及测试用例,但也许这是SpecFlow应该如何工作,是吗?
  4. 到目前为止,对于此测试用例,我的.feature文件中包含以下内容。

        Feature: RegisterUser
        In order to register a user successfully
        A valid username, email, password (and confirmation password) must be entered
    
    
    @register
    Scenario: Valid Register User
        Given I have entered a username "testing", an email "testing@gmail.com", a password "123456" and a confirmation password "123456"
        When I press submit
        Then the response code should be 200
        And the user should be added to the database with verfied set to False
    
    Background: 
        Given I have registered a user "testing", "testing@gmail.com", "123456" and "123456"
    
    @register
    Scenario: Username Already Taken
        Given I have entered a username "testing", an email "testing1@gmail.com", a password "123456" and a confirmation password "123456"
        When I press submit
        Then the response code should be 400
    

    我的步骤文件是这样的:

    using NUnit.Framework;
    using System;
    using System.Net.Http;
    using TechTalk.SpecFlow;
    
    namespace Tests.Specs
    {
    [Binding]
    public class RegisterUserSteps
    {
        private RegisterUserModel userInfo = new RegisterUserModel();
        private RegisterUserController user = new RegisterUserController()
        {
            Request = new HttpRequestMessage(),
            Configuration = new System.Web.Http.HttpConfiguration()
        };
        private ApiResponse response = new ApiResponse();
        private static string userTableName = "usersTable";
        private static string userTablePartitionKey = "USER_INFO";
        private static string userTableRowKey = "testing@gmail.com";
    
    
        [Given(@"I have entered a username ""(.*)"", an email ""(.*)"", a password ""(.*)"" and a confirmation password ""(.*)""")]
        public void GivenIHaveEnteredAUsernameAnEmailAPasswordAndAConfirmationPassword(string p0, string p1, string p2, string p3)
        {
            userInfo.Username = p0;
            userInfo.Email = p1;
            userInfo.Password = p2;
            userInfo.ConfirmPassword = p3;
        }
    
        [When(@"I press submit")]
        public void WhenIPressSubmit()
        {
            response = user.Register(userInfo);
        }
    
        [Then(@"the response code should be (.*)")]
        public void ThenTheResponseCodeShouldBe(int p0)
        {
            Assert.AreEqual(p0, response.Status);
        }
    
    
        [Then(@"the user should be added to the database with verfied set to False")]
        public void ThenTheUserShouldBeAddedToTheDatabaseWithVerfiedSetToFalse()
        {
            UserEntity user = AzureUtilities.RetrieveEntity<UserEntity>(userTableName, userTablePartitionKey, userTableRowKey);
            Assert.IsNotNull(user);
            Assert.AreEqual(userInfo.Username, user.Username);
            Assert.AreEqual(userInfo.Email, user.RowKey);
            Assert.IsFalse(user.Verified);
        }
    
        [Given(@"I have registered a user ""(.*)"", ""(.*)"", ""(.*)"" and ""(.*)""")]
        public void GivenIHaveRegisteredAUserAnd(string p0, string p1, string p2, string p3)
        {
            //Using methods here that have been defined in previous steps
            GivenIHaveEnteredAUsernameAnEmailAPasswordAndAConfirmationPassword(p0, p1, p2, p3);
            WhenIPressSubmit();
            ThenTheResponseCodeShouldBe(200);
        }
    

1 个答案:

答案 0 :(得分:1)

我认为您正试图跳转到使用背景而不实际拥有在您的方案中常见的东西。

SpecFlow非常强大,只要您为步骤创建一致的结构,就可以重复使用很多步骤。

让我们从您的功能开始。此功能类似于开发人员脚本。功能应该代表一些业务目标。忍受我,因为我不知道你的业务目标是什么

Feature: Register New Users
    In order to perform task that only registered users can do
    As a web site user
    I want to register for the site

既然我们知道为什么要编写代码,我们就可以开始制定方案来实现这一目标了。

Given I am not registered for the site
And I have filled out the registration form as follows
| Field                 | Value             |
| username              | testing           |
| email                 | testing@gmail.com |
| password              | 123456            |
| password confirmation | 123456            |
When I submit the registration
Then the response code should be '200'
And the database should have an unverified user

这会创建一些sstepscenarios,但是您将立即重用,因为您现在传递变量以填写注册表单并检查响应代码。

所以现在你的用户名已被采用场景可以改为

Scenario: Username taken
Given a user has registered with the following information
| Field                 | Value             |
| username              | testing           |
| email                 | testing@gmail.com |
| password              | 123456            |
| password confirmation | 123456            |
And I have filled out the registration form as follows
| Field                 | Value             |
| username              | testing           |
| email                 | testing@gmail.com |
| password              | 123456            |
| password confirmation | 123456            |
When I submit the registration
Then the response code should be '400'

所以现在你需要实现的唯一步骤是第一步

鉴于用户已注册以下信息

由于步骤只是代码,因此您可以从第一个方案中调用已定义的步骤,如此

    [Given(@"a user has registered with the following information")]
    public void GivenAUserHasRegisteredWithTheFollowingInformation(Table table)
    {
        GivenIHaveFilledOutTheRegistrationFormAsFollows(table);
        WhenISubmitTheRegistration();
        ThenTheResponseCodeShouldBe(200)
    }

对于Hooks,我将这些用于更多技术方面。我在使用Selenium的测试中做了很多。我创建了一个钩子@web,它在场景之前运行以启动Selenium并在场景完成后将其拆除。我已经对API测试做了同样的事情,也许你想在场景之前启动一个HttpClient,之后就会崩溃。在实际开始测试之前,您还可以执行清除数据库或清除和播种到已知状态的操作。

我希望这能解答你所有的问题。