Rails shared spec 'helpers' by /app and /spec

197 views Asked by At

{Hi} all,

we have the following conundrum with my team:

We have some rspec tests in /spec for which we need to stub requests and at the same time we have cypress tests (a cypress controller in /app folder) where we also need to stub those requests. We use webmock; the stubbing logic turned out to be identical when we added the stubs. So a question arose, why not put the stubbing logic in one place and include it both in the specs and in the cypress controller? (Is this an anti-pattern by the way?)

We want to do that but where do we put this common logic? We have no good arguments for one place over another, so any thoughts would be appreciated.

Edit: A little bit explanation:

  1. We have rails back-end (GraphQL API) and React front-end(served by a rails controller) (as a result of a long almost finished transition from rails mvc app).
  2. We have many rspec tests where we stubbed all the requests to external systems
  3. We have some (increasing) cypress integration tests, and the way cypress works is it simulates real behavior by opening a browser and simulates user actions, and it makes requests to the back-end. We have some cypress controllers in the app there to handle things such as creation of test data (authentication, factories and mocks controllers). And in the mocks controller we stub requests to a few external systems (as we did in the rspec tests)

Code example:

  describe('Stripe Checkout', () => {
  const amount = 1000;
  const endOfYear = moment().endOf('year').format('MMYY');

  beforeEach(() => {
    cy.cleanBackend();
    cy.cleanDatabase();
  });

  ['usd', 'gbp', 'cad'].forEach((currency) => {
    it(`Can checkout with Stripe ${currency}`, () => {
      cy.mockBackendRequests({
        stub_stripe_requests: true,
        stub_digitalinvoice_requests: true,
        stub_currency_layer_requests: [currency],
      });

      cy.newGive('en', currency).then(({ intl, url }) => {
        const { formatMessage } = intl;
        cy.createRecords([
          ['create', 'donation_target', 'approved'],
          ['create', 'donation_target', 'approved', { id: Cypress.env('GIVE_CHARITY') }],
        ]).then(([donationTarget]) => {
          cy.visit(url(`/donation-targets/${donationTarget.id}`));

          addToCart({ donationTarget, amount, currency, intl });
          goTroughCartPage({ donationTarget, amount, currency, url, intl });

          fillPersonalForm({ intl, fillAddress: true });
          cy.location('pathname').should('equal', url(`/checkout/payment`));

          cy.get('main iframe')
            .iframe()
            .within(() => {
              cy.get('[name="cardnumber"]')
                .should('exist')
                .and('not.be.disabled')
                .type('4242424242424242', { force: true, delay: 15 });
              cy.get('[name="exp-date"]').focus().type(endOfYear);
              cy.get('[name="cvc"]').focus().type('111');
              cy.get('[name="postal"]').focus().type('22222');
            });

          cy.contains(formatMessage({ id: 'components.Payment.continue' })).click();

          finishCheckout({ donationTarget, amount, currency, url, intl });
        });
      });
    });
  });
});

So createRecords talks with the factories controller and creates the necessary test data, and mockBackendRequests talks with the mocks controller and creates all the necessary stubs. as example the cleanBackend and cleanDatabase commands are also calls to controllers which do as they say.

So, is our setup wrong, and if it's not, how do we achieve the shared logic idea in my question?

0

There are 0 answers