bencompton / jest-cucumber

Execute Gherkin scenarios in Jest
Apache License 2.0
662 stars 118 forks source link

Logging middleware #125

Open DonnyVerduijn opened 3 years ago

DonnyVerduijn commented 3 years ago

Hi. I would like to propose adding additional utilities to this repo to support debugging. For example, something like below would be really beneficial in logging the return values of the step definition callbacks and manage the visibility of the logging statements. It could also serve other purposes like applying default assertions, but that might be out of scope for this repo.

EDIT: I took the plunge to create a reusable middleware factory and it turned out pretty well. Its API is still a little crazy so that might need some extra work.

const createMiddleware: DefineStepMiddleware = (
  create: (stepFn: DefineStepFunction, fnName: string) => DefineStepFunction
) => (stepDefinitionFn) => ({ pending, ...rest }) =>
  stepDefinitionFn({
    pending,
    ...Object.entries(rest).reduce(
      (acc, [fnName, stepFn]: [fnName: string, stepFn: DefineStepFunction]) =>
        Object.assign(acc, {
          [fnName]: create(stepFn, fnName)
        }),
      rest
    )
  });

const withLogging = createMiddleware((stepFn, fnName) => (matcher, cb) =>
  stepFn(matcher, (...args) => {
    console.log(fnName.green['bold'], matcher.toString().green);
    return cb(...args);
  })
);

It then can be used to wrap the test definition.

test(
  '1 + 2 = 3',
  withLogging(({ given, when, then }) => {
    let number = 1;

    given('number is 1', () => {
      expect(number).toBe(1);
    });
    when('adding 2', () => {
      number += 2;
    });
    then('number should be 3', () => {
      expect(number).toBe(3)
    });
  })
);

Please let me know if anyone is interested in having this.

bencompton commented 3 years ago

Middleware like this is an interesting idea, especially if there were a publicly exported middleware creation function that provided a simple API for creating middleware that abstracted away all of the stepsDefinitionCallback stuff.

I will say that your use case might be better served with proper reporting functionality, though. See #27.

DonnyVerduijn commented 3 years ago

You are right about the logging part. This is definitely not something you want to pollute your tests with. However, i got really excited about this idea of putting middleware layers between those callbacks. It opens up a lot of possibilities of drying up tests and improving their readability.

I just recently started picking up TDD, so my awareness about the available tools is most likely insufficient. I know about Jest its mocking/stubbing features and its ability to output to JSON, but i don't know if they really solve the problem of drying up our tests. jest-cucumber also afforts an autobinding feature which to my awareness makes step definitions reusable on the test suite level?

Providing an API to attach middleware to step definition handlers at multiple levels in the test suite hierarchy, would relate very well to the concept of Gherkin its backgrounds. It might be a good starting point to reason about any inconsistencies between Gherkin its concepts and the tooling jest-cucumber provides, in order to support these concepts.

bencompton commented 3 years ago

I definitely agree that some sort of a middleware / plug-in architecture in general would be helpful in this library. The goal with Jest Cucumber is to not stray too far from what people would expect from Jest and Cucumber, but at the same time, a lot of people have specific preferences or requirements that this library can't easily accommodate without imposing these opinions on everyone else.

jest-cucumber also afforts an autobinding feature which to my awareness makes step definitions reusable on the test suite level?

In typical Jest tests, you use describe and it or test blocks to make your tests readable and easy to understand. The default in Jest Cucumber is designed to accomplish the same thing where the contents of your feature files are fully represented in your Jest tests and you don't have to continuously cross-reference your Jest tests and feature files, don't have to deal with global step matching, etc. At the same time, Jest Cucumber ensures that your Jest tests stay in sync with your feature files, and also generates code suggestions when your Jests tests don't match your feature files. This allows teams to practice ATDT with business-readable feature files, and then automate them in Jest without worrying about Jest tests and feature files getting out of sync.

Some people would prefer that Jest Cucumber work more like Cucumber.js, which is what autoBindSteps is designed to accomplish. Rather than defining Jest tests that mirror the feature file, autoBindSteps allows you to define only step definitions and it creates your Jest tests for you automatically.

There are pros and cons to each approach, and it comes down to being a matter of preference, so I'd suggest you look over the docs / examples and decide which you prefer. Basically, do you prefer more Jest-like tests, or more Cucumber-like tests.