Как я могу сделать так, чтобы specflow хорошо справлялся с датами / временем? - PullRequest
5 голосов
/ 04 марта 2011

Я бы хотел написать такие тесты:

Background:
  Given a user signs up for a 30 day account

Scenario: access before expiry
  When they login in 29 days
  Then they will be let in

Scenario: access after expiry
  When they login in 31 days
  Then they will be asked to renew

Scenario: access after acounnt deleted
  When they login in 2 years time
  Then they will be asked to register for a new account

Как мне выполнить проверку на стороне спецификаций?

Редактировать: как одни и те же определения шагов справляются как с «31 днем», так и с «2 годами»

Ответы [ 6 ]

3 голосов
/ 14 марта 2011

Я столкнулся с аналогичной проблемой, связанной с тем, как справиться с относительными датами и временем в SpecFlow, и подошел к нему, поддержав нечеткие даты в рамках спецификаций.Я использовал код из этого ответа: Нечеткий элемент управления выбора времени в C # .NET? , который позволил бы вам выразить то, что вы хотите, следующим образом:

Background:
    Given a user signs up for a 30 day account

Scenario: access before expiry
    When they login in the next 29 days
    Then they will be let in

Scenario: access after expiry
    When they login in the next 31 days
    Then they will be asked to renew

Scenario: access after account deleted
    When they login in the next 2 years
    Then they will be asked to register for a new account

С определением шага, напримерas:

[When(@"they login in the (.*)")]
public void WhenTheyLoginIn(string loginDateTimeString)
{
    DateTime loginDateTime = FuzzyDateTime.Parse(loginDateTimeString);

    // TODO: Use loginDateTime
}

Если вам не нравится синтаксис для нечетких дат, вы можете изменить регулярные выражения в коде FuzzyDateTime для соответствия.

3 голосов
/ 04 марта 2011

Сборка этого файла .feature создаст код для тестов. Затем вам нужно подключить каждый шаг к методу. Самый простой способ сделать это,

1: отладка тестов, тест не пройдёт как неокончательный. Глядя на спецификацию потока результатов теста, вы можете добавить шаблон для этого теста. сообщение об ошибке будет выглядеть примерно так

Assert.Inconclusive не удалось. Не найдено соответствующее определение шага для одного или нескольких шагов.

    [Binding]
public class StepDefinition1
{
    [Given(@"a user signs up for a 30 day account")]
    public void GivenAUserSignsUpForA30DayAccount()
    {
    }

    [When(@"they login in 29 days")]
    public void WhenTheyLoginIn29Days()
    {
        ScenarioContext.Current.Pending();
    }

    [Then(@"they will be let in")]
    public void ThenTheyWillBeLetIn()
    {
        ScenarioContext.Current.Pending();
    }
}

2: Скопируйте это в новый файл определения шага specflow, который в основном является классом модульного тестирования, заполненным атрибутами specflow. Теперь есть несколько приемов, которые вы можете сделать, чтобы помочь вам. в методе GivenAUserSignsUpForA30DayAccount я бы создал пользователя, который будет использоваться в тесте с пробной учетной записью на 30 дней. Закрытый член будет работать нормально, поэтому вы можете получить к ним доступ между методами, но это работает, только если все методы находятся в одном классе. если вы попытаетесь повторно использовать методы между несколькими объектами / классами, вам нужно будет сохранить объект в ScenarioContext

3: при запуске теста specflow он ищет метод, имеющий соответствующий атрибут с той же строкой. Хитрость в том, что вы можете передавать параметры методу, используя подстановочные знаки в Атрибуте метода. Есть 2 разных картотеки

(. *) Означает, что вы передаете строку этому методу (\ d +) означает, что вы передаете int этому методу.

Поскольку ваш метод When является общим, вы можете использовать его с такими параметрами.

    [When(@"they login in (\d+) days")]
    public void WhenTheyLoginInDays(int daysRemaining)
    {
        Account.DaysRemaining = daysRemaining;
    }

4: наконец, добавьте ваши Asserts в метод Then, чтобы конечный результат выглядел примерно так. (обратите внимание, что лично я немного реструктурирую формулировку функции и передам ожидаемые результаты таким образом, чтобы логика тестирования не была такой же скверной, как в моем примере, посмотрите схемы сценариев для тестов, управляемых данными)

    [Binding]
public class StepDefinition1
{
    UserAccount user;

    [Given(@"a user signs up for a 30 day account")]
    public void GivenAUserSignsUpForA30DayAccount()
    {
        user = AccountController.CreateNewUser("bob", "password", AccountType.Trial);
    }

    [When(@"they login in (\d+) days")]
    public void WhenTheyLoginInDays(int daysRemaining)
    {
        Account.DaysRemaining = daysRemaining;
    }

    [Then(@"they will (.*)")]
    public void ThenTheyWillBeLetIn(string expected)
    {
        //check to see which test we are doing and then assert to see the expected result.
        if(string.Compare(expected, "be let in", true)
            Assert.AreEqual(LoginResult.Passed, LoginService.Login);
        if(string.Compare(expected, "be asked to renew", true)
            Assert.AreEqual(LoginResult.Passed, LoginService.Login);

    }
}
1 голос
/ 11 ноября 2016

Думаю, вы ищете StepArgumentTransformation .

Чтобы справиться с «через 31 день», в документах есть для вас:

[Binding]
public class Transforms
{
    [StepArgumentTransformation(@"in (\d+) days?")]
    public DateTime InXDaysTransform(int days)
   {
      return DateTime.Today.AddDays(days);
   }
}

А для «через 2 года» вы можете увидеть шаблон ...

    [StepArgumentTransformation(@"in (\d+) years?")]
    public DateTime InXYearsTransform(int years)
   {
      return DateTime.Today.AddYears(years);
   }
1 голос
/ 16 января 2012

Я знаю, что долго боролся с этим. Потребовалось некоторое время, чтобы изменить мой существующий код, но убил все ссылки на DateTime.Now и заменил их интерфейсом, который, как я мог, сделал Mock, сделал все в миллион раз проще для тестирования (и изменения позже). Я создал IDateTimeService, который имеет один метод "GetCurrent ()". Теперь все мои данные шаги могут сказать:

Given the current date is '2/4/12'
And the user's account was created on '1/24/12'

Тогда я могу сделать проверку дальности намного проще.

Шаг для текущей даты выглядит следующим образом:

[Given(@"Given the current date is '(.*)'")]
public void GivenTheCurrentDateIs(string date)
{
     var dateServiceMock = new Mock<IDateTimeService>();
     dateServiceMock.Setup(ds => ds.GetCurrent()).Returns(DateTime.Parse(date));
     ScenarioContext.Current.Add("dateService", dateServiceMock);
}
1 голос
/ 14 марта 2011
> how can the same step definitions cope with both "31 days" and "2 years time"

Если ваши правила не требуют специальной обработки для рабочего дня, рождества, выходных, ... вы можете изменить @ Nitro52-s ответ на:

[When(@"they login in (\d+) days")]
public void WhenTheyLoginInDays(int daysRemaining)
{
    Account.RegristrationDate = DateTime.ToDay().SubtractDays(daysRemaining);
    Account.VerificationDate = DateTime.ToDay();    
}

Может быть, вы также можете подумать о переформулированиитакие сценарии

Scenario: access before expiry
  When they login on '2010-01-01'
    And TodayIs '2010-01-29'
  Then they will be let in
0 голосов
/ 06 апреля 2011

Попробуйте использовать Моль и заглушите DateTime.Now, чтобы каждый раз возвращать одну и ту же дату.Одна из лучших особенностей Moles - это возможность превращать dang рядом с чем угодно в делегата времени выполнения, который вы можете изолировать.Единственным недостатком является то, что он может работать медленнее в зависимости от выбранной вами реализации (заглушка против moled).Я только начинаю погружаться в это, так что прими мое предложение с крошкой соли.

...