-
-
Notifications
You must be signed in to change notification settings - Fork 6.2k
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 BeforeInsert with a mocked Repository #5593
Comments
see #1267 for the general discussion on testing |
I think it's kind of tricky, because I will need to play with the listeners used by the Broadcaster and the metadata, is there a function to get the metadata directly from the class without a connection to a DB ? |
@devniel we have the same problem. My work around is query after the hooks and check the result. I know its more work. 😢 |
@artoodeeto I was able to get it 😁, I have created a testing module to make it easy to integrate in new tests https://github.com/devniel/nestjs-typeorm-testing, don't hesitate to contribute if you want. |
@devniel thanks man. This works even though im not using nestjs? |
@artoodeeto I have updated the module so it can be used in non-nestjs projects. there is an example here https://github.com/devniel/example-typeorm-testing. |
@devniel you are my hero. much love man! ❤️ |
The way I approached this is to just call the beforeInsert function on an instance of the entity directly in a test file and then test the object shape after calling the function. E.g. The entity export class Meta {
@Column('varchar', { name: 'META_QUALITY_CD', nullable: true, length: 50 })
metaQualityCd!: string | null;
@Column
...
@BeforeInsert()
generateInsertMeta() {
const now = new Date(new Date().toUTCString());
this.metaQualityCd = null;
this.metaActionCd = MetaActionCD.Insert;
this.metaCreatedDttm = now;
this.metaCreatorNm = WEBAPP_USER_NAME;
this.metaChangedDttm = now;
this.metaChangedByNm = WEBAPP_USER_NAME;
}
} The test describe('MetaEntity', () => {
describe('beforeInsert', () => {
it('should set meta fields as expected', () => {
const meta = new Meta();
MockDate.set('2020-01-22');
const now = new Date();
const expected: Partial<Meta> = {
metaQualityCd: null,
metaActionCd: MetaActionCD.Insert,
metaCreatedDttm: now,
metaCreatorNm: WEBAPP_USER_NAME,
metaChangedDttm: now,
metaChangedByNm: WEBAPP_USER_NAME,
};
// just call the BeforeInsert function directly (it is simply an instance function) and then assert the instance shape
meta.generateInsertMeta();
expect(meta).toEqual(expected);
});
});
}); Not sure if this will cover all cases but IMO it should be sufficient just to test the method like this rather than needing to involve the rest of TypeORM at all (by mocking typeorm connection all you are doing really is testing typeORMs behaviour for ensuring that the decorators are called as expected?) EDIT Though, thinking about it, this could lead to issues where you are not calling the correct function in our test (i.e. if the BeforeInsert decorator is actually over another instance method). Maybe we could mock the BeforeInsert decorator in the test somehow so that it picks up the correct method and then we use that one in our tests. |
I had a go with playing with mocks to see if something like that could be achieved and came up with the below: // inside test.ts
// create a map to track the listener method names that have Decorators set
interface Listeners<E> {
BeforeInsert?: keyof E;
BeforeUpdate?: keyof E;
}
const listenerMap: Listeners<MyEntity> = {};
import * as typeorm from 'typeorm';
// use jest.spyOn to mock only the listeners
// mock the implementation of the decorators to capture the decorated method names and set them in the map
const mockBeforeInsert = jest.spyOn(typeorm, 'BeforeInsert').mockImplementation(() => (_: any, fnName: string) => {
listenerMap.BeforeInsert = fnName as keyof MyEntity;
});
const mockBeforeUpdate = jest.spyOn(typeorm, 'BeforeUpdate').mockImplementation(() => (_: any, fnName: string) => {
listenerMap.BeforeUpdate = fnName as keyof MyEntity;
});
// The mocks must be created prior to the import of the entity
import { MyEntity } from './MyEntity';
...
it('should call the correct BeforeInsert method', () => {
const entity = new MyEntity();
const modifiedEntity = {...};
// find the BeforeInsert method name
const beforeInsertMethodNm = listenerMap.BeforeInsert!;
// call this method on your instance
(meta[beforeInsertMethodNm as keyof Entity] as Function)();
expect(mockBeforeInsert).toHaveBeenCalled();
expect(entity).toEqual(modifiedEntity);
}); This will use mock implementations of BeforeInsert and BeforeUpdate decorators to extract the method names of functions that have been decorated, then these can be called on your instance of your entity. e.g. @BeforeInsert
doBeforeUpdate() { ... }
// then the mock will set listeners.BeforeUpdate as 'doBeforeUpdate' There can be some extra cleanup here to types probably. Also Listeners.BeforeInsert/BeforeUpdate should be arrays in reality as several functions could be decorated |
Issue type:
[x] question
[ ] bug report
[ ] feature request
[ ] documentation issue
Database system/driver:
[ ]
cordova
[ ]
mongodb
[ ]
mssql
[ ]
mysql
/mariadb
[ ]
oracle
[ ]
postgres
[ ]
cockroachdb
[x]
sqlite
[ ]
sqljs
[ ]
react-native
[ ]
expo
TypeORM version:
[x]
latest
[ ]
@next
[ ]
0.x.x
(or put your version here)Hi, I'm trying to check that the method decorated with @BeforeInsert in my User entity is called, but with a mocked repository it's not working because it seems that @BeforeInsert it's called when doing the actual insertion in the database, so maybe I need to mock one lower layer. What do you think ? (actually I'm trying to do this with nestjs).
The text was updated successfully, but these errors were encountered: