Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to test middleware? #18

Open
riggedCoinflip opened this issue May 22, 2021 · 5 comments
Open

How to test middleware? #18

riggedCoinflip opened this issue May 22, 2021 · 5 comments
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@riggedCoinflip
Copy link

I am searching for a way to test my authorization middleware.

I use express middleware to validate my authorization token.

//isAuth
//simplified
module.exports = (req, res, next) => {
    const token = req.headers['x-auth-token'];
    if (token invalid) {
        req.user = {
            isAuth: false,
        }
    else {
        req.user = {
            isAuth: true,
            _id: decodedToken._id,
            name: decodedToken.username,
            role: decodedToken.role,
    }
    next();
}

I then apply the middleware

const app = express();
app.use(isAuth);

function createApollo() {
    const apollo = new ApolloServer({
        schema: graphqlSchema,
        context: ({req, res}) => ({req, res}),
    });
    apollo.applyMiddleware({app, path: "/graphql"});
    return apollo
}

Lastly, I wrap my graphql-compose resolvers that require authentication with this function:

module.exports = (resolvers) => {
    Object.keys(resolvers).forEach((k) => {
        resolvers[k] = resolvers[k].wrapResolve(next => async rp => {
            if (!rp.context.req.user.isAuth) {
                throw new Error('You must login to view this.');
            }
            return next(rp)
        })
    })
    return resolvers
}

In the end I got it working like this:

setOptions({
    request: {
        user: {
            isAuth: true,
            _id: decodedToken._id,
            name: decodedToken.username,
            role: decodedToken.role,
        }
    },
});

but that bypasses my isAuth middleware.

Is there any way, using this or any other package to test middleware as well?
We could add apollo-client or alike as a dev-dependency and test the queries as if there were directly from frontend, but there has to be a better way.

@deduced
Copy link

deduced commented May 31, 2021

I am having a very similar issue, but in my case I am trying to mock the isAuth and call next() as a mock implementation, but I don't think it's being made available in the test. https://stackoverflow.com/questions/67768435/proper-way-to-test-type-graphql-middleware-with-jest.

@deduced
Copy link

deduced commented May 31, 2021

@riggedCoinflip have you tried mocking the import? https://stackoverflow.com/questions/60722281/testing-an-express-middleware-with-jest?

jest.mock("pathToYourAuthModule", () => {
  return {
    isAuth: jest.fn((req,res,next) => next()), //and/or you can set your req.user.isAuth to true
  };
});

if your authentication module has other functionality you want to keep unmocked, see https://jestjs.io/docs/jest-object#jestrequireactualmodulename

@vitorbal vitorbal added help wanted Extra attention is needed question Further information is requested labels Jun 1, 2021
@nogashimoni
Copy link

I'm having the same issue.
Why aren't express middlewares applied? Is this by design or is it a bug?

@vitorbal vitorbal added bug Something isn't working and removed question Further information is requested labels Jun 14, 2021
@vitorbal
Copy link
Contributor

@nogashimoni @riggedCoinflip howdy folks! It looks like this is a bug with the way this library simulates the graphql requests. I have a failing test that reproduces the issue, so I'll be looking into possible solutions this week.

@vitorbal
Copy link
Contributor

vitorbal commented Jun 15, 2021

So I've been taking another look at this and I don't think it will be possible to solve it without a couple of breaking changes to the API.
Specifically, we'd have to make it so this library starts the graphql server and then sends actual http requests to it. Today, we use some of the ApolloServer APIs to simulate a graphql request instead.

@nogashimoni @riggedCoinflip one alternative would be to refactor your middlewares so they can run from within the context callback, like this:

const apolloServer = new ApolloServer({
  schema,
  context: ({ req }) => {
    return isAuth(req);
  },
});

If that's not okay, then I would recommend looking into a library like supertest that help you test an http server.

Meanwhile, I'm going to do some thinking on what the future of this library is. ApolloServer now exposes an executeOperation API that should make it easier to solve what this library initially set out to solve, but also wouldn't support testing express middlewares.
Perhaps we can rework this library to provide sugar on top of executeOperation, and additionally provide an API to help test apollo-server-express end-to-end too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

4 participants