I’m having a hard time to mock/test this useLazyQuery case; the hook in the screen:
const [
getSpecificReport,
{ loading: contentLoading, error: contentError, data: content },
] = useLazyQuery<SpecificReportResponse>(
SPECIFIC_REPORT(testResultsData?.getTestResults.testType),
{
client: cmsClient, // <- this is a specific ApolloClient
fetchPolicy: "network-only",
onCompleted: () => {
setScreenData();
},
onError: (err) => {
// (...) omitted for simplification
},
}
);
The mock:
const mocks = [{
request: {
query: SPECIFIC_REPORT('Report Title'),
fetchPolicy: 'network-only',
},
result: {
data: {
allReports: [getReportTestData()],
} as SpecificReportResponse,
},
}]
The test:
...
const { getByText, getAllByText } = render(
<MockedProvider
mocks={mocks}
addTypename={false}
>
<ResultsScreen {...mockProps} />
</MockedProvider>
);
await waitFor(() => {
new Promise((resolve) => setTimeout(resolve, 3000));
expect(getByText(/Something/)).toBeTruthy();
(...)
}
...
What happens is that the screen (ResultsScreen) just acts as if not received the data, i.e. the first expectation fails.
I noticed that if I take off the specific client from the hook, the test works fine - but not the screen, which depends on that.
I wonder if I should pass a “mocked client” to the mocks[0].request or something - I already tried to do it, but no success so far.
Does anyone have any ideas?
Thanks in advance!
I solved it by mocking the client like this (didn't change anything else):
...
const mockCmsClient = new ApolloClient({
link: ApolloLink.from([ApolloLink.empty(), ApolloLink.empty(), ApolloLink.empty()]),
cache: new InMemoryCache(),
});
jest.mock('../../my-module-with-exported-client', () => ({
...jest.requireActual('../../my-module-with-exported-client'),
cmsClient: mockCmsClient,
}));
...
Related
I'm not able to mock chained function of sequelize.
In following example I can mock Query 1, but not Query 2
something.service.ts
// Query 1
await this.table2.findAll<table2>({
attributes: [
'field1'
],
where: {
id: someId
},
});
// Query 2
// returns []
let bill1: any = await this.table2.sequelize.query(`
SELECT
aa.field1,
bg.field2
FROM
table1 aa,
table2 bg
WHERE
bg.id = '${billId}'
AND
aa.id = bg.aggr_id;
`);
something.service.spec.ts
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
{
provide: getModelToken(table2),
useValue: {
// mock successful for query 1
findAll: jest.fn(() => [{}]),
// mock fails for query 2
sequelize: jest.fn().mockReturnValue([]),
query: jest.fn().mockReturnValue([]),
'sequelize.query': jest.fn().mockReturnValue([]),
},
}
],
}).compile();
With this code I'm receiving (for Query 2)
TypeError: this.table2.sequelize.query is not a function
I tried with following code, no luck
sequelize: jest.fn().mockReturnValue([]),
query: jest.fn().mockReturnValue([]),
'sequelize.query': jest.fn().mockReturnValue([]),
sequelize: jest.fn().mockReturnValue({
query: jest.fn(() => [])
})
You can utilize jest.fn().mockReturnThis() to mock the chained function in jest. I have tested this on mocking the TypeORM repository, something like this:
repository.mock.ts
export const mockedRepository = {
find: jest.fn(),
createQueryBuilder: jest.fn(() => ({ // createQueryBuilder contains several chaining methods
innerJoinAndSelect: jest.fn().mockReturnThis(),
getMany: jest.fn(),
})),
};
Somewhere in your service for example:
test.service.ts
//
async findAll(){
return await this.repository
.createQueryBuilder('tableName')
.innerJoinAndSelect('tableName.relation','relation' )
.getMany();
}
//
And finally the unit test spec:
test.service.spec.ts
const module = await Test.createTestingModule({
providers: [
TestService,
{
provide: getRepositoryToken(Test),
useValue: mockedRepository,
}
],
}).compile();
testService =
module.get<TestService>(TestService);
testRepository = module.get<Repository<Test>>(
getRepositoryToken(Test),
);
});
describe('when findAll is called', () => {
beforeEach(() => {
mockedRepository.createQueryBuilder.getMany.mockResolvedValue([]);
});
it('should call innerJoinAndSelect method once', async () => {
await testService.findAll();
expect(mockedRepository.createQueryBuilder.innerJoinAndSelect).toHaveBeenCalledTimes(1);
});
it('should return an empty array', async () => {
expect(await testService.findAll()).toBe([]);
});
});
This is not a real working example but I hope you get the idea.
Issue was with the problem statement itself, this.table.sequelize is an object NOT a function to be chained, following solution worked to mock it.
sequelize: { query: jest.fn(() => []) }
To mock chained functions Farista's solution works.
I use react-native with graphql.
I have a query and tried to use refetch.
const { data: updatePhoto, refetch } = useQuery(SEE_PHOTO_QUERY, {
variables: {
id: photoId,
},
});
when after I edit my comment, I want to refetch this query and put it on setState in order to change UI.
const onEditValid = async ({ comments }) => {
const commentId = await AsyncStorage.getItem("#commentId");
await editCommentMutation({
variables: {
id: parseInt(commentId),
payload: comments,
},
update: updateEditComment,
});
};
const updateEditComment = async (cache, result) => {
const {
data: {
editComment: { error, ok, id },
},
} = result;
if (ok) {
const commentId = await AsyncStorage.getItem("#commentId");
const { comments } = getValues();
await textRef.current.clear();
await refetch();
setState(updatePhoto);
await cache.modify({
id: `Comment:${commentId}`,
fields: {
payload(prev) {
return comments;
},
},
});
}
};
But UI doesn't change.
I tried to change UI by modifying cache and refetching data. But both fails for a week.. :(
I also raised the question about fail of cache modify
=> React Native: `cache.modity` doesn't work
But no one answers.
I really need your help.. please help me
I would like to know how to test the code inside a .subscribe callback, with the subscription being on a NgRx store selector.
Environment: Angular 13, RxJs 7+, NgRx 13, Jest 27
Consider
my-component.ts
...
ngOnInit {
this.myValue = true;
this.store.select(mySelector).pipe(filter(data => data.attribute === true)).subscribe(data => {
this.myValue = false; // I want to test this
}
}
...
my-component.spec.ts
describe('MyComponent', () => {
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>;
let store: MockStore;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [MyComponent],
imports: [...],
providers: [
provideMockStore({
initialState: { myFeature: { } },
}),
],
}).compileComponents();
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
store = TestBed.inject(MockStore);
});
it('should perform animation & redirect to /dashboard if login successful', () => {
store.setState({
myFeature: {
...
attribute: true,
},
});
expect(component.myValue).toBe(false);
});
This works, but it's random. Since this is asynchronous I could test myValue before the subscribe callback has been called and the test would fail, for example if my subscribe callback takes time to do stuff, like so (I'm adding a delay of 500ms) :
...
ngOnInit {
this.myValue = true;
this.store.select(mySelector).pipe(filter(data => data.attribute === true), delay(500)).subscribe(data => {
this.myValue = false; // I want to test this
}
}
...
This fails.
How can I wait the callback to perform before testing my value ? I could wait an arbitrary time like 1 sec before testing, but it could break at any time in the future it's not robust enough. Like :
it('should perform animation & redirect to /dashboard if login successful',
async () => {
store.setState({
myFeature: {
...
attribute: true,
},
});
await lastValueFrom(timer(600)); //rxjs 7
expect(component.myValue).toBe(false);
});
Thanks for your help
Use waitForAsync and fixture.whenStable to ensure completion of async tasks before evaluating the expect.
it('should perform animation & redirect to /dashboard if login successful', waitForAsync(() => {
store.setState({
myFeature: {
...
attribute: true,
},
});
fixture.whenStable()
.then(() => expect(component.myValue).toBe(false));
}));
I'm using vue2 with composition Api, vuex and apollo client to request a graphql API and I have problems when mocking composable functions with jest
// store-service.ts
export function apolloQueryService(): {
// do some graphql stuff
return { result, loading, error };
}
// store-module.ts
import { apolloQueryService } from 'store-service'
export StoreModule {
state: ()=> ({
result: {}
}),
actions: {
fetchData({commit}) {
const { result, loading, error } = apolloQueryService()
commit('setState', result);
}
},
mutations: {
setState(state, result): {
state.result = result
}
}
}
The Test:
// store-module.spec.ts
import { StoreModule } from store-module.ts
const store = StoreModule
describe('store-module.ts', () => {
beforeEach(() => {
jest.mock('store-service', () => ({
apolloQueryService: jest.fn().mockReturnValue({
result: { value: 'foo' }, loading: false, error: {}
})
}))
})
test('action', async ()=> {
const commit = jest.fn();
await store.actions.fetchData({ commit });
expect(commit).toHaveBeenCalledWith('setData', { value: 'foo' });
})
}
The test fails, because the commit gets called with ('setData', { value: undefined }) which is the result from the original apolloQueryService. My Mock doesn't seem to work. Am I doing something wrong? Appreciate any help, thanks!
Try this :
// store-module.spec.ts
import { StoreModule } from store-module.ts
// first mock the module. use the absolute path to store-service.ts from the project root
jest.mock('store-service');
// then you import the mocked module.
import { apolloQueryService } from 'store-service';
// finally, you add the mock return values for the mock module
apolloQueryService.mockReturnValue({
result: { value: 'foo' }, loading: false, error: {}
});
/* if the import order above creates a problem for you,
you can extract the first step (jest.mock) to an external setup file.
You should do this if you are supposed to mock it in all tests anyway.
https://jestjs.io/docs/configuration#setupfiles-array */
const store = StoreModule
describe('store-module.ts', () => {
test('action', async ()=> {
const commit = jest.fn();
await store.actions.fetchData({ commit });
expect(commit).toHaveBeenCalledWith('setData', { value: 'foo' });
})
}
I am passing params from my API to vue-head but every time I do that it send me undefined in the head this is the code:
export default {
data: () => ({
errors: [],
programs: [],
}),
methods: {
getProgram() {
this.api.http.get(`videos/program/${this.programSlug}`)
.then(response => {
this.programs = response.data
})
.catch(error => {
this.errors = error
});
}
},
head: {
title: function() {
return {
inner: this.programs.name,
separator: '|',
complement: 'Canal 10'
};
}
}
}
any idea what I am doing wrong with my code??
First verify you are fetching the information correctly. Use console log and go to network tab and verify you are fetching the data correct, you might have to comment out vue-head. But what I think is that the problem might be due to vue-head rendering before the api call finishes then no data is being passed.
If you are using vue-router this can be easily solved with beforeRouteEnter() hook. But if not! apparently vue-head has an event that you can emit to update the component after render.
I haven't tried this but it should work. you can add the function below to your methods and call it after the promise is resolved i.e in the then closure.
methods: {
getProgram() {
this.api.http.get(`videos/program/${this.programSlug}`)
.then(response => {
this.programs = response.data
this.$emit('updateHead')
})
.catch(error => {
this.errors = error
});
}
}