I followed the official documentation on testing ngxs selectors (https://ngxs.gitbook.io/ngxs/recipes/unit-testing#testing-selectors), however it doesn't cover how to unittest dynamic selectors created with createSelector.
My normal selector just gets the state as an argument so I can easly test it by passing a prepared state and comparing the output.
#Selector()
static nachweise(state: NachweisStateModel) {
return state.nachweise;
}
//Setup state
const state = {...};
//Compare expectations
expect(NachweisState.nachweise(state)).toEqual(...);
My dynamic selector looks like this:
#Selector()
static nachweisById(id: string) {
return createSelector([NachweisState], state => {
return state.nachweise.find(nachweis => nachweis.id === id);
});
}
The only parameter it gets is the id by which it selects, but not the state. The State is automagically passed in by specifying it as the first parameter to createSelector and I don't know how I should test this selector.
It seems that the documentation has been updated:
it('should select requested animal names from state', () => {
const zooState = {
animals: [
{ type: 'zebra', name: 'Andy'},
{ type: 'panda', name: 'Betty'},
{ type: 'zebra', name: 'Crystal'},
{ type: 'panda', name: 'Donny'},
]
};
const value = ZooSelectors.animalNames('zebra')(zooState);
expect(value).toEqual(['Andy', 'Crystal']);
});
Related
I have two databases that I need to interact with in my code. I have a simple function that takes an object and writes it to my PostgreSQL database using Prisma. I've tested the function with Postman, and it works perfectly, but when I try to execute it using a Jest mock (using the singleton pattern found in the Prisma unit testing guide), it returns undefined indicating that it didn't interact with the database and create the new record. Here's my code:
/prisma/clinical-schema.prisma
generator client {
provider = "prisma-client-js"
output = "./generated/clinical"
}
datasource clinicalDatabase {
provider = "postgresql"
url = "postgresql://postgres:postgres#localhost:5432/clinical-data?schema=public"
}
model pcc_webhook_update {
id Int #id #default(autoincrement())
event_type String
organization_id Int
facility_id Int
patient_id Int
resource_id String?
webhook_date DateTime #default(now()) #clinicalDatabase.Timestamptz(6)
status pcc_webhook_update_status #default(pending)
status_changed_date DateTime? #clinicalDatabase.Timestamptz(6)
error_count Int #default(0)
##unique([organization_id, facility_id, patient_id, resource_id, event_type, status])
}
enum pcc_webhook_update_status {
pending
processing
processed
error
}
/prisma/clinical-client.ts
import { PrismaClient } from './generated/clinical';
const prismaClinical = new PrismaClient();
export default prismaClinical;
/testing/prisma-clinical-mock.ts
import { PrismaClient } from '../prisma/generated/clinical';
import { mockDeep, mockReset, DeepMockProxy } from 'jest-mock-extended';
import prisma from '../prisma/clinical-client';
jest.mock('../prisma/clinical-client', () => ({
__esModule: true,
default: mockDeep<PrismaClient>()
}));
beforeEach(() => {
mockReset(prismaClinicalMock);
});
export const prismaClinicalMock = prisma as unknown as DeepMockProxy<PrismaClient>;
Everything up to this point follows the conventions outlined by the Prisma unit testing docs. The only modification I made was to make it database specific. Below is my function and tests. The request object in handle-pcc-webhooks.ts is a sample http request object, the body of which contains the webhook data I care about.
/functions/handle-pcc-webhooks/handler.ts
import prismaClinical from '../../../prisma/clinical-client';
import { pcc_webhook_update } from '../../../prisma/generated/clinical';
import { requestObject } from './handler.types';
export const handlePccWebhook = async (request: requestObject) => {
try {
const webhook = JSON.parse(request.body);
// if the webhook doesn't include a resource id array, set it to an array with an empty string to ensure processing and avoid violating
// the multi-column unique constraint on the table
const { resourceId: resourceIds = [''] } = webhook;
let records = [];
for (const resourceId of resourceIds) {
// update an existing record if one exists in the pending state, otherwise create a new entry
const record: pcc_webhook_update = await prismaClinical.pcc_webhook_update.upsert({
where: {
organization_id_facility_id_patient_id_resource_id_event_type_status: {
organization_id: webhook.orgId,
facility_id: webhook.facId,
patient_id: webhook.patientId,
resource_id: resourceId,
event_type: webhook.eventType,
status: 'pending'
}
},
update: {
webhook_date: new Date()
},
create: {
event_type: webhook.eventType,
organization_id: webhook.orgId,
facility_id: webhook.facId,
patient_id: webhook.patientId,
resource_id: resourceId,
status: 'pending' // not needed
}
});
records.push(record);
}
return records;
} catch (error) {
console.error(error);
}
};
/functions/handle-pcc-webhooks/handler.spec.ts
import fs from 'fs';
import path from 'path';
import MockDate from 'mockdate';
import { prismaClinicalMock } from '../../../testing/prisma-clinical-mock';
import { createAllergyAddRecord } from './__mocks__/allergy';
import { requestObject } from './handler.types';
import { handlePccWebhook } from './handler';
describe('allergy.add', () => {
let requestObject: requestObject;
let allergyAddRecord: any;
beforeAll(() => {
requestObject = getRequestObject('allergy.add');
});
beforeEach(() => {
MockDate.set(new Date('1/1/2022'));
allergyAddRecord = createAllergyAddRecord(new Date());
});
afterEach(() => {
MockDate.reset();
});
test('should create an allergy.add database entry', async() => {
prismaClinicalMock.pcc_webhook_update.create.mockResolvedValue(allergyAddRecord);
// this is where I would expect handlePccWebhook to return the newly created database
// record, but instead it returns undefined. If I run the function outside of this
// unit test, with the same input value, it functions perfectly
await expect(handlePccWebhook(requestObject)).resolves.toEqual([allergyAddRecord]);
});
});
// This just builds a request object with the current webhook being tested
function getRequestObject(webhookType: string) {
// read the contents of request object file as a buffer, then convert it to JSON
const rawRequestObject = fs.readFileSync(path.resolve(__dirname, '../../sample-data/handle-pcc-webhook-request.json'));
const requestObject: requestObject = JSON.parse(rawRequestObject.toString());
// read the contents of the webhook file as a buffer, then convert it to a string
const rawWebhook = fs.readFileSync(path.resolve(__dirname, `../../sample-data/${webhookType}.json`));
const webhookString = rawWebhook.toString();
// set the body of the request object to the contents of the target webhook
requestObject.body = webhookString;
return requestObject;
}
Finally, here is the result of running the unit test:
So after banging my had against the wall for a few hours, I figured out the issue. In my handler.spec.ts file, I had the following line:
prismaClinicalMock.pcc_webhook_update.create.mockResolvedValue(allergyAddRecord);
what that does is mock the value returned for any create functions run using Prisma. The issue is that my function is using an upsert function, which I wasn't explicitly mocking, thus returning undefined. I changed the above line to
prismaClinicalMock.pcc_webhook_update.upsert.mockResolvedValue(allergyAddRecord);
and it started working.
I try use pubsub in apollo server & apollo client. but subscribed data is only null.
client dependency
"#apollo/react-hooks": "^3.1.5",
"apollo-boost": "^0.4.9",
"apollo-link-ws": "^1.0.20",
"graphql": "^15.0.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.1",
"styled-components": "^5.1.1",
"subscriptions-transport-ws": "^0.9.16",
"typescript": "~3.7.2"
server dependency
"apollo-server": "^2.14.1",
"graphql": "^15.0.0",
"merge-graphql-schemas": "^1.7.8",
"ts-node": "^8.10.2",
"tsconfig-paths": "^3.9.0",
"typescript": "^3.9.3"
// apolloClient.ts
import { ApolloClient, HttpLink, InMemoryCache, split } from 'apollo-boost'
import { WebSocketLink } from 'apollo-link-ws'
import { getMainDefinition } from 'apollo-utilities'
const wsLink = new WebSocketLink({
uri: 'ws://localhost:4000/graphql',
options: {
reconnect: true
}
})
const httpLink = new HttpLink({
uri: 'http://localhost:4000'
})
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink,
)
const cache = new InMemoryCache()
const client = new ApolloClient({
cache: cache,
link: link,
})
export default client
// subscribe.ts
const ON_PUT_UNIT = gql`
subscription onPutUnit($code: String!) {
onPutUnit(code: $code)
}
`
const onPutResult = useSubscription(
ON_PUT_UNIT,
{ variables: {
code: code,
}}
)
// in is only null!!
console.log('subscribe', onPutResult)
-server-
onPutUnit.ts
type Subscription {
onPutUnit(code: String!): Room
}
import { pubsub } from '#src/index'
const { withFilter } = require('apollo-server')
export default {
Subscription: {
onPutUnit: {
subscribe: withFilter(
() => pubsub.asyncIterator(['PUT_UNIT']),
(payload: any, variables: any) => {
// no problem in payload & variable data
return payload.code === variables.code
}
)
}
},
}
putUnit.ts
type Mutation {
putUnit(code: String!, x: Int!, y: Int!, userName: String!): Room!
}
export default {
Mutation: {
putUnit: async (_: any, args: args) => {
const { code, x, y, userName } = args
const room = findRoom(code)
console.log(room) // no problem. normal data.
pubsub.publish('PUT_UNIT', room)
return room
},
},
}
Is it some problem? subscribe event is normally reached to client when publish. but data is is only null. I can't fine the reason.
You only specified a subscribe function for onPutUnit, without specifying a resolve function. That means the field utilizes the default resolver.
The default resolver just looks for a property with the same name as the field on the parent object (the first parameter passed to the resolver) and returns that. If there is no property on the parent object with the same name as the field, then the field resolves to null. The parent object is the value the parent field resolved to. For example, if we have a query like this:
{
user {
name
}
}
whatever the resolver for user returns will be the parent value provided to the resolver for name (if user returns a Promise, it's whatever the Promise resolved to).
But what about user? It has no parent field because it's a root field. In this case, user is passed the rootValue you set when initializing the ApolloServer (or {} if you didn't).
With subscriptions, this works a bit differently because whatever value you publish is actually passed to the resolver as the root value. That means you can take advantage of the default resolver by publishing an object with a property that matches the field name:
pubsub.publish('PUT_UNIT', { onPutUnit: ... })
if you don't do that, though, you'll need to provide a resolve function that transforms the payload you published. For example, if we do:
pubsub.publish('PUT_UNIT', 'FOOBAR')
Then our resolver map needs to look something like this:
const resolvers = {
Subscription: {
onPutUnit: {
subscribe: ...,
resolve: (root) => {
console.log(root) // 'FOOBAR'
// return whatever you want onPutUnit to resolve to
}
}
},
}
I'm trying to build an Apollo useSubscription hook that uses onSubscriptionData.
I've looked in the Apollo docs, but I haven't yet an example.
E.g. something like:
const { loading, error, data } = useSubscription(
INCOMING_MESSAGES_SUBSCRIPTION_QUERY,
{
variables: {"localUserId": Meteor.userId()},
onSubscriptionData: myFunctionThatRunsWhenSubscriptionDataArrives
}
);
That can't be right yet, because it doesn't include OnSubscriptionDataOptions<TData>, which is mentioned in the Apollo docs.
What is the correct way to build a useSubscription hook that uses onSubscriptionData?
The onSubscriptionData function is passed a single options parameter of the type OnSubscriptionDataOptions. The options object has two properties:
client -- the ApolloClient instance used to query the server
subscriptionData -- an object with the following properties: loading, data, error
Example usage:
const { loading, error, data } = useSubscription(
INCOMING_MESSAGES_SUBSCRIPTION_QUERY,
{
variables: {"localUserId": Meteor.userId()},
onSubscriptionData: ({ subscriptionData: { data } }) => {
// do something with `data` here
}
},
)
We have a function like this:
export function* postPermitApplicationRequest() {
try {
const uris = yield select(state => getUris(state));
.... (more yields hereafter)
We test this function with Jest and Chai as follows:
...
const action = { type: Action.POST_PERMIT_APPLICATION };
const generator = postPermitApplicationRequest(action);
it('must select uris from state', () => {
const nextCall = generator.next().value;
const uris = select(state => getUris(state));
expect(nextCall).to.deep.equal(uris);
});
However the expect fails:
AssertionError: expected { Object (##redux-saga/IO, SELECT) } to deeply equal { Object (##redux-saga/IO, SELECT) }
at Assertion.assertEqual (node_modules/chai/lib/chai/core/assertions.js:485:19)
at Assertion.ctx.(anonymous function) [as equal] (node_modules/chai/lib/chai/utils/addMethod.js:41:25)
at Object.<anonymous> (src/app/pa/PermitApplicationServiceSagas.test.js:20:43)
at process._tickCallback (internal/process/next_tick.js:109:7)
The two objects both look like:
{ '##redux-saga/IO': true,
SELECT: { selector: [Function], args: [] } }
However the selector functions are different. The one that is the outcome of generator.next() contains code coverage skip hints:
function (state) {/* istanbul ignore next */cov_zrpq42gyn.f[12]++;cov_zrpq42gyn.s[19]++;return (/* istanbul ignore next */(0, _Selectors.getUris)(state));}
while the original function doesn't:
function (state) {return (0, _Selectors.getUris)(state);}
It looks like generator.next() adds these hints and the assertion fails
What do we wrong here?
We use redux-saga 0.14.8
The tests fail because in your saga and your test you create a new functions every time you execute the code. These both functions will be compared but are not the same instance.
You can simply use select(getUris) in your saga and your test because both will reference to the same function.
Your saga:
export function* postPermitApplicationRequest() {
try {
const uris = yield select(getUris);
.... (more yields hereafter)
Your test:
...
const action = { type: Action.POST_PERMIT_APPLICATION };
const generator = postPermitApplicationRequest(action);
it('must select uris from state', () => {
const nextCall = generator.next().value;
const uris = select(getUris);
expect(nextCall).to.deep.equal(uris);
});
Quick and shortly I have following problem:
I have following two actions within a component in Ember:
createData: function(user) {
let collection = [];
for (let i = 0; i < user.posts.length; i++) {
let data = this.send('createSingleData',user.posts[i], user, 'post');
console.log(data);
collection.push(data);
}
return collection;
},
createSingleData: function(data, user, type) {
let entitySkeleton = {
name: data.place.name,
belongsTo: user.id,
position: {
data.place.location.longitude,
data.place.location.latitude
}
};
console.log(entitySkeleton);
return entitySkeleton;
}
the first log - within createSingleData, right before returning the logged value - writes the entitySkeleton as Object into the console - as expected.
However, the console.log(data) - within createData - writes 'undefined' to the console.
Is there any aspect of asynchrounosity I didn't respect?
P.S.:
I also logged any paramater within createSingleData, they are all set properly.
The variable collection also only gets pushed 'undefined'.
You cannot return the value from action, instead you can set property from the action.
how to return values from actions in emberjs
actions: {
PrintSomething: function() {
let obj = [{a: 'raj'}, {a: 'Prudvi'}, {a : 'thimappa'}]
console.log('before', obj);
this.send('returnSomething', obj);
console.log('after calling action', this.get('returnvalue'));
},
returnSomething: function(obj) {
obj.push({a: 'FSDFSDF'})
var data = obj;
this.set('returnvalue', data);
}
}