MSW do not intercept GraphQL requests - graphql

I've tried every possible solution described on the internet, but nothing seems to help.
I created a mock server and am trying to intercept a GraphQL call:
./setup/setup-server.ts
const handlers = [
graphql.query<GetOperation, GetOperationVariables>(
'GetOperation'
(req, res, ctx) => {
// Nothing showing in console
console.log(req.variables);
return res(
ctx.data({
operation: [] // mocked operation
})
);
}
),
]
./graphql/use-get-operation.ts
export const GET_OPERATION_BY_ID_DEFAULT = gql`
query GetOperation($idOrContractNumber: String!) {
operation(input: { idOrContractNumber: $idOrContractNumber }) {
id
modality
totaltaxes
exchangeRate
}
}
`;
The call is not intercepted.
I've already tried:
Import jest-fetch-mock in the configuration file;
Start the server in setupFiles.js (as exemplified in the documentation);
Start the server in the file directly in the test;
Use regex instead of string in the operation name;
Nothing seems to work.
I tried to create both queries and mutations and it didn't work.
MSW version used: 0.33.0

Related

Apollo client ignoring argument for GQL REST query

I have a query that is defined like this:
export const getProjectCosts = gql`
query GetProjectCosts($projectId: Int) {
ProjectCostList #rest(type: "ProjectCostList", path: "ProjectCosts/{args.projectId}") {
id
projectId
cost
description
costType
budgetYear
createdByUser
createdDate
}
}
`;
export const useGetProjectCostsListQuery = (baseOptions?: QueryHookOptions<ProjectCostList>) => {
const options = { ...baseOptions };
return useQuery<ProjectCostList>(getProjectCosts, options);
};
I call it like this:
const {
loading: projectCostLoading,
error: projectCostError,
data: projectCostData,
} = useGetProjectCostsListQuery({
variables: {
projectId: args.defaultValues.id,
},
});
I have verified in the debugger that the argument is sent correctly.
However, in the browser I see a warning:
Warning: RestLink caught an error while unpacking ProjectCosts/{args.projectId}|args.projectId This tends to happen if you forgot to pass a parameter needed for creating an #rest(path, or if RestLink was configured to deeply unpack a path parameter that wasn't provided. This message will only log once per detected instance. Trouble-shooting hint: check #rest(path: and the variables provided to this query.
(anonymous) # restLink.ts:567
And a request is sent to api/ProjectCosts/ instead of api/ProjectCosts/1234, which not surprisingly fails with a HTTP 404 error.
Is this a bug or am I doing something wrong?
I found this issue on Github, which looks very similar to what I'm experiencing.
We're using a custom .NET backend.
If it's a bug, is there a workaround?
The problem was that the parameter has to be declared twice.
This is the solution:
export const getProjectCosts = gql`
query GetProjectCosts($projectId: Int!) {
ProjectCostList(projectId: $projectId) #rest(type: "ProjectCostList", path: "ProjectCosts/{args.projectId}") {

What is the proper way to unit test Service with NestJS/Elastic

Im trying to unit test a Service that uses elastic search. I want to make sure I am using the right techniques.
I am new user to many areas of this problem, so most of my attempts have been from reading other problems similar to this and trying out the ones that make sense in my use case. I believe I am missing a field within the createTestingModule. Also sometimes I see providers: [Service] and others components: [Service].
const module: TestingModule = await Test.createTestingModule({
providers: [PoolJobService],
}).compile()
This is the current error I have:
Nest can't resolve dependencies of the PoolJobService (?).
Please make sure that the argument at index [0]
is available in the _RootTestModule context.
Here is my code:
PoolJobService
import { Injectable } from '#nestjs/common'
import { ElasticSearchService } from '../ElasticSearch/ElasticSearchService'
#Injectable()
export class PoolJobService {
constructor(private readonly esService: ElasticSearchService) {}
async getPoolJobs() {
return this.esService.getElasticSearchData('pool/job')
}
}
PoolJobService.spec.ts
import { Test, TestingModule } from '#nestjs/testing'
import { PoolJobService } from './PoolJobService'
describe('PoolJobService', () => {
let poolJobService: PoolJobService
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [PoolJobService],
}).compile()
poolJobService = module.get<PoolJobService>(PoolJobService)
})
it('should be defined', () => {
expect(poolJobService).toBeDefined()
})
I could also use some insight on this, but haven't been able to properly test this because of the current issue
it('should return all PoolJobs', async () => {
jest
.spyOn(poolJobService, 'getPoolJobs')
.mockImplementation(() => Promise.resolve([]))
expect(await poolJobService.getPoolJobs()).resolves.toEqual([])
})
})
First off, you're correct about using providers. Components is an Angular specific thing that does not exist in Nest. The closest thing we have are controllers.
What you should be doing for a unit test is testing what the return of a single function is without digging deeper into the code base itself. In the example you've provided you would want to mock out your ElasticSearchServices with a jest.mock and assert the return of the PoolJobService method.
Nest provides a very nice way for us to do this with Test.createTestingModule as you've already pointed out. Your solution would look similar to the following:
PoolJobService.spec.ts
import { Test, TestingModule } from '#nestjs/testing'
import { PoolJobService } from './PoolJobService'
import { ElasticSearchService } from '../ElasticSearch/ElasticSearchService'
describe('PoolJobService', () => {
let poolJobService: PoolJobService
let elasticService: ElasticSearchService // this line is optional, but I find it useful when overriding mocking functionality
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
PoolJobService,
{
provide: ElasticSearchService,
useValue: {
getElasticSearchData: jest.fn()
}
}
],
}).compile()
poolJobService = module.get<PoolJobService>(PoolJobService)
elasticService = module.get<ElasticSearchService>(ElasticSearchService)
})
it('should be defined', () => {
expect(poolJobService).toBeDefined()
})
it('should give the expected return', async () => {
elasticService.getElasticSearchData = jest.fn().mockReturnValue({data: 'your object here'})
const poolJobs = await poolJobService.getPoolJobs()
expect(poolJobs).toEqual({data: 'your object here'})
})
You could achieve the same functionality with a jest.spy instead of a mock, but that is up to you on how you want to implement the functionality.
As a basic rule, whatever is in your constructor, you will need to mock it, and as long as you mock it, whatever is in the mocked object's constructor can be ignored. Happy testing!
EDIT 6/27/2019
About why we mock ElasticSearchService: A unit test is designed to test a specific segment of code and not make interactions with code outside of the tested function. In this case, we are testing the function getPoolJobs of the PoolJobService class. This means that we don't really need to go all out and connect to a database or external server as this could make our tests slow/prone to breaking if the server is down/modify data we don't want to modify. Instead, we mock out the external dependencies (ElasticSearchService) to return a value that we can control (in theory this will look very similar to real data, but for the context of this question I made it a string). Then we test that getPoolJobs returns the value that ElasticSearchService's getElasticSearchData function returns, as that is the functionality of this function.
This seems rather trivial in this case and may not seem useful, but when there starts to be business logic after the external call then it becomes clear why we would want to mock. Say that we have some sort of data transformation to make the string uppercase before we return from the getPoolJobs method
export class PoolJobService {
constructor(private readonly elasticSearchService: ElasticSearchService) {}
getPoolJobs(data: any): string {
const returnData = this.elasticSearchService.getElasticSearchData(data);
return returnData.toUpperCase();
}
}
From here in the test we can tell getElasticSearchData what to return and easily assert that getPoolJobs does it's necessary logic (asserting that the string really is upperCased) without worrying about the logic inside getElasticSearchData or about making any network calls. For a function that does nothing but return another functions output, it does feel a little bit like cheating on your tests, but in reality you aren't. You're following the testing patterns used by most others in the community.
When you move on to integration and e2e tests, then you'll want to have your external callouts and make sure that your search query is returning what you expect, but that is outside the scope of unit testing.

Says Jasmine spy is not being called, but I can see that its being called

I can't figure out why Jasmine is claiming that the function I'm spying on isn't being called, especially since it is logging in buildLinksObj when called through and not calling when I remove .and.callThrough() I feel like I've written similar code a bunch of times before without any problem. I'm using Jasmine 2.9
The error message I'm getting is:
1) addToLinks should call buildLinksObj if its given an object with children
it should add the personalized links to PageApp.meta.analytics.links
Expected spy buildLinksObj to have been called.
at UserContext.<anonymous> (http://localhost:9877webpack:///tests/specs/common/FetchPersonalContent.spec.js:854:0 <- tests/app-mcom.js:104553:48)
Here's the except of my code:
FetchPersonalContent.js
const buildLinksObj = (responseObj = {}, targetObj, PageApp) => {
console.log('it logs in buildLinksObj') // This is logging!
}
const addToLinks = (responseArr, personalizedLinks) => {
responseArr.forEach((media) => {
const type = media.type;
const typeObj = media[type];
buildLinksObj(typeObj, personalizedLinks, PageApp);
if (typeObj && typeObj.children) {
console.log('has children!')
console.log('typeObj.children is: ', typeObj.children);
typeObj.children.forEach((child) => {
console.log('has a child')
buildLinksObj(child, personalizedLinks, PageApp);
console.log('buildLinksObj was definitely called. what the heck?')
});
}
});
}
export {buildLinksObj, addToLinks, FetchPersonalContent as default,
};
FetchPersonalContent.spec.js
import * as FetchPersonalContent from '../../../src/FetchPersonalContent'; // my path is definitely correct
describe('it should add the personalized links to PageApp.meta.analytics.links', () => {
it('addToLinks should call buildLinksObj if its given an object with children ', () => {
spyOn(FetchPersonalContent, 'buildLinksObj').and.callThrough();
FetchPersonalContent.addToLinks([{
"personalId": 30718,
"type": "carousel",
"carousel": {}
}], {});
expect(FetchPersonalContent.buildLinksObj).toHaveBeenCalled();
});
});
I'd really appreciate any help!
I have a feeling FetchPersonalContent.buildLinksObj in the spec file is not pointing to the same instance as buildLinksObj in the FetchPersonalContent.js file.
Why is export {FetchPersonalContent as default} required? I am assuming you have shared the complete content of FetchPersonalContent.js in your question.
Possible solutions:
You can try removing FetchPersonalContent from the export statement.
Or
Instead of
export {buildLinksObj, addToLinks, FetchPersonalContent as default,
};
You can directly export the constants in FetchPersonalContent.js file.

How to unit test graphql query/mutation error with Mocha and Chai?

Since graphql error is not an standard Error. It's a GraphQLError
I can't figure out how to write unit test when graphql query/mutation throw an exception.
Here is my try:
it('should throw an error when lack of "id" argument for user query', async () => {
const body = {
query: `
query user{
user {
id
name
}
}
`
};
try {
const actualValue = await rp(body);
} catch (err) {
logger.error(err);
}
// logger.info(actualValue);
expect(1).to.be.equal(1);
// expect(actualValue).to.throw()
});
I found some tests in graphql.js repo. https://github.com/graphql/graphql-js/blob/master/src/tests/starWarsQuery-test.js#L393
But I think the way they test the query/mutation error is not correct.
They just do a deep equal with the error. But before running the test suites, how do I know the error data structure like locations: [{ line: 5, column: 13 }],? Maybe I should use snapshot testing so that I don't need to know the error data structure?
Check this package https://github.com/EasyGraphQL/easygraphql-tester there is an example with mocha and chai on the documentation

Why is my graphql Higher Order Component fire the options 11 times with the Apollo Client?

I'm using apollo client in an exponent react native app and have noticed that the graphql options method gets run 11 times, why is that? Is that an error or a performance problem? Is that normal? Is it running the query 11 times as well?
...
#graphql(getEventGql,{
options: ({route}) => {
console.log('why does this log 11 times', route.params);
return {
variables: {
eventId: route.params.eventId,
}
}
},
})
#graphql(joinEventGql)
#connect((state) => ({ user: state.user }))
export default class EventDetailScreen extends Component {
...
Looking at the sample from the documentation http://dev.apollodata.com/react/queries.html
Typically, variables to the query will be configured by the props of
the wrapper component; where ever the component is used in your
application, the caller would pass arguments. So options can be a
function that takes the props of the outer component (ownProps by
convention):
// The caller could do something like:
<ProfileWithData avatarSize={300} />
// And our HOC could look like:
const ProfileWithData = graphql(CurrentUserForLayout, {
options: ({ avatarSize }) => ({ variables: { avatarSize } }),
})(Profile);
By default, graphql will attempt to pick up any missing variables from
the query from ownProps. So in our example above, we could have used
the simpler ProfileWithData = graphql(CurrentUserForLayout)(Profile);.
However, if you need to change the name of a variable, or compute the
value (or just want to be more explicit about things), the options
function is the place to do it.

Resources