2

我目前正在学习使用 SpecFlow,并想编写一个功能,其中包括注册用户的测试。

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

我的下一个测试案例将是一个无效案例,我将执行上述步骤,然后尝试使用相同的用户名注册用户,这应该会返回错误。从我找到的示例中可以理解,我可以在 .feature 文件中使用“背景”来指定要在 .feature 文件中的每个场景之前执行的步骤。这将涉及在后台部分创建一个步骤来注册用户并调用在有效注册用户案例中创建的方法。这个问题对我有所帮助,但也为我提出了更多问题,因为我无法理解可用标签和挂钩的正确用法。

我的问题是:

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

到目前为止,对于这个测试用例,我的 .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);
    }
4

1 回答 1

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

这会创建许多步骤场景,但您将立即获得重用,因为您现在正在传递变量以填写注册表并检查响应代码。

所以现在您的用户名已经被使用的场景可以更改为

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 并在之后拆除。您还可以在实际开始测试之前执行诸如清除数据库或清除和播种到已知状态之类的操作。

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

于 2017-12-21T19:31:35.987 回答