Tyler-Keith-Thompson / CucumberSwift

A lightweight swift Cucumber implementation
https://tyler-keith-thompson.github.io/CucumberSwift/documentation/cucumberswift/
MIT License
74 stars 19 forks source link

use Feature and Scenario for better test results #59

Open bitsmakerde opened 1 year ago

bitsmakerde commented 1 year ago

@Tyler-Keith-Thompson I try make a better test case struct in my project, but I'm not sure which is the way to get this.

Here my first idea:

I create one StepImplementation and import here my Features:

import CucumberSwift

extension Cucumber: StepImplementation {
    public var bundle: Bundle {
        class FindMe {}
        return Bundle(for: FindMe.self)
    }

    public func setupSteps() {
        SaveImageLocallyTests().steps()
        DeleteImageLocallyTests().steps()
    }
}

Inside every feature I have something like this:

import CucumberSwift
import CucumberSwiftExpressions

class DeleteImageLocallyTests: XCTestCase {
    func steps() {
        Feature("Delete an image") {
            Scenario("Delete an image locally") {
                Given("the user has a local stored image" as CucumberExpression) { _, _ in
                }

                When("he delete the image" as CucumberExpression) { _, _ in
                }

                Then("the local store should be empty" as CucumberExpression) { _, _ in
                }
            }
        }
    }
}

For me is to work with feature and scenario very useful to have a good strutted code. but if I look to the test results in Xcode I get a different list, which not use my feature and scenario. Is there a best practice to have all in a good struct?

image
Tyler-Keith-Thompson commented 1 year ago

I will try and play with some options to give you a better answer, but for right this second you seem to be using a mixture of the DSL and feature files. I really recommend only using one. Your current code isn't quite supported as is.

If you're going to use the DSL, you don't need to use the Given/When/Then methods that take a string or regex, for example

Feature("Delete an image") {
    Scenario("Delete an image locally") {
        Given(the: userHasALocallyStoredImage())
        When(the: imageIsDeleted())
        Then(the: localStoreShouldBeEmpty())
    }
}

func userHasALocallyStoredImage() { XCTFail("Implement the step") }
func imageIsDeleted() { XCTFail("Implement the step") }
func localStoreShouldBeEmpty { XCTFail("Implement the step") }

If you're going with .feature files, I don't recommend wrapping your steps in the Feature/Scenario DSL. So instead do:

func steps() {
    Given("the user has a local stored image" as CucumberExpression) { _, _ in }
    When("he delete the image" as CucumberExpression) { _, _ in }
    Then("the local store should be empty" as CucumberExpression) { _, _ in }
}

It's possible that some of what you're seeing with all those duplicate entries is really a byproduct of mixing and matching the 2 methods of executing Gherkin.

That said, I am sadly very limited here. Apple doesn't really give access to this in a sane way. The only way CucumberSwift is able to show up in that navigator at all is to use the Objective-C runtime to dynamically create classes. The | character was used to delineate between features and scenarios in those test case names. I'll leave this issue open so I can explore a bit and see if we can do better.

bitsmakerde commented 1 year ago

@Tyler-Keith-Thompson thank you for this nice answer.

But both ways look for me not so "sexy" at the moment.

Why I think so. I like to see a one to one relation for the .feature to the source code. But in both ways something is missing like the Feature / Scenario or the Given/When/Then description text.

One other point I find out is, that I can't start a Scenario separate.

What do you think if a Scenario is warp inside a func test_... so Xcode can find this a test run it and can show it automatic in the navigator. For this case for me its fine only to see the scenario is failed and being inspect the detail I see there its fails.

I try the first way, but for it not really work. CucumberSwift says's alway not implementation for my feature.

Will it be a problem if I stay by my code?

bitsmakerde commented 1 year ago

I comment the duplicate code out. But it not looking nice.

  // swiftlint:disable todo
  // TODO: - Feature("Delete an image")
  // MARK: - Scenario("Delete an image locally")

        Given("the user has selected an image to save" as CucumberExpression) { _, _ in
            ...
        }

        When("the image is saved to the selected location on the device's storage" as CucumberExpression) { _, _ in
            ...
        }

        Then("the image becomes persistent on the phone" as CucumberExpression) { _, _ in
           ...
        }

This looks like this in Xcode

image