'specflow/cucumber with complex logic

I am considering using SpecFlow for a new automation project. Since SpecFlow is similar to Cucumber in the Java world this question applies to Cucumber as well.

In real world applications there are lists of complex objects and tests are required to look just for specific object in those lists and only for specific fields of.

For example, a chat application displays a list of messages, a message being a complex object comprising of a date, user name, user icon image, text, and maybe other complex objects like images, tables, etc.

Now, one test may require just to check that the chat is not empty. Other test may require just to check that a message from a specific user is present. And another one just to check for a message with a specific text. The amount of verification rules can grow into many tens.

Of course, one way to deal with that is to implement a "step" for each verification rule, hence writing tens of steps just to discover that yet another one is needed... :(

I found that a better way is to use NUnit Constrains (Hamcrest Matchers in Java) to define those rules, for example:

[Test]
public void ShouldNotBeEmpty() {
  ...
  Assert.That(chatMessages, Is.Not.Empty);
}
[Test]
public void ShouldHaveMessageFrom(string user) {
  ...
  Assert.That(chatMessages, Contains.Item(new Message() with User=user));
  // sometimes the User field maybe a complex object too...
}
[Test]
public void ShouldHaveMessage(string text) {
  ...
  Assert.That(chatMessages, Contains.Item(new Message() with Text=text));
}

This way the mechanism that brings chatMessages can work with any kind of verification rule. Hence in a BDD framework, one could make a single step to work for all:

public void Then_the_chat(IConstraint matcher) {
  Assert.That(someHowLoadChatMessagesHere, matcher);
}

Is there any way in SpecFlow/Cucumber to have these rules mapped to Gerkin syntax?



Solution 1:[1]

Code reuse is not the biggest concern for a behavior-driven test. Accurately describing the business use case is what a BDD test should do, so repetitive code is more acceptable. The reality is that you do end up with a large number of step definitions. This is normal and expected for BDD testing.

Within the realm of a chat application, I see three options for writing steps that correspond to the unit test assertions in your question:

  1. Unit Test:

    [Test]
    public void ShouldNotBeEmpty() {
        ...
        Assert.That(chatMessages, Is.Not.Empty);
    }
    

    Gherkin:

    Then the chat messages should not be empty
    
  2. Unit Test:

    [Test]
    public void ShouldHaveMessageFrom(string user) {
        ...
        Assert.That(chatMessages, Contains.Item(new Message() with User=user));
        // sometimes the User field maybe a complex object too...
    }
    

    Gherkin:

    Then the user should have a chat message from "Greg"
    
  3. Unit Test:

    [Test]
    public void ShouldHaveMessage(string text) {
        ...
        Assert.That(chatMessages, Contains.Item(new Message() with Text=text));
    }
    

    Gherkin:

    Then the user should have a chat message with the following text:
        """
        Hi, everyone!
    
        How is the weather, today?
        """
    
  4. Unit Test:

    public void Then_the_chat(IConstraint matcher) {
        Assert.That(someHowLoadChatMessagesHere, matcher);
    }
    

    This gets a little more difficult. Consider using a data table to specify a more complex object in your assertion in Gherkin:

    Then the user should have the following chat messages:
        | Sender | Date Sent           | Message |
        | Greg   | 5/2/2022 9:24:18 AM | ...     |
        | Sarah  | 5/2/2022 9:25:39 AM | ...     |
    

    SpecFlow will pass a Table object as the last parameter to this step definition. You can use the SpecFlow.Assist Table helpers to compare the data table to your expected messages.

This gives you some options to think about. Which one you choose should be determined by how well the step and scenario reads in Gherkin. Without more information, this is all I can provide. Feel free to try these out and post new questions concerning more specific problems.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Greg Burghardt