Reputation: 1200
I am getting the following error while unit testing a function that creates a record in the database model.
Model not initialized: Member "create" cannot be called. "UserModel" needs to be added to a Sequelize instance.
I found a github issue regarding this on https://github.com/RobinBuschmann/sequelize-typescript/issues/688
I tried a solution of using validateOnly: true
and it introduces another error:
this.lib.Database is not a constructor
The model class looks something like this:
@Table({ tableName: 'User' })
export class UserModel extends Model {
@AutoIncrement
@PrimaryKey
@Column(DataType.INTEGER)
public id: number;
@Unique
@AllowNull(false)
@Column(DataType.STRING(100))
public email!: string;
@AllowNull(false)
@Column(DataType.TEXT)
public password!: string;
}
The function that I want to test looks like:
public async register(registerRequest: RegisterRequest) {
return UserModel.create({
email: registerRequest.email,
password: registerRequest.password
})
}
// A sample unit test looks like:
test('1# Register user', async () => {
const userService = new UserAccountService()
jest.spyOn(UserModel, 'create')
const registerRequest: RegisterRequest = {
email: '[email protected]',
password: '1234'
}
const registeredUser = await userService.register(registerRequest);
expect(UserModel.create).toHaveBeenCalledTimes(1);
expect(registeredUser).toBeInstanceOf(UserModel);
expect(registeredUser.email).toEqual('[email protected]')
});
Can someone help me on how to unit test with such Sequelize database calls? I know we can use mockImplementationOnce()
or mockResolvedValue()
with a fake UserModel
object, but what if I have a lot of database calls in a function with multiple models, mocking or creating all fake model objects is very annoying and that leads to creating complicated and big test cases, which apparently is a bad sign. Is there any other way how we can unit test with Sequelize model functions?
Upvotes: 3
Views: 10164
Reputation: 1200
I found following solution for this problem:
We can create fake/mock sequelize instance and can initialize all the models using sqlite3 package. This would be very easy for unit and integration testing as well. Since testing a function that contains lot of database calls can be very tedious if we mock every database call, we can use this fake sequelize instance without any database and yet can perform CRUD operation without throwing any error or warning.
describe('Testing', () => {
let mockedSequelize: Sequelize;
beforeEach(async () => {
mockedSequelize = new Sequelize({
database: '<any name>',
dialect: 'sqlite',
username: 'root',
password: '',
validateOnly: true,
models: [__dirname + '/models'],
});
await mockedSequelize.sync({ force: true });
})
afterEach(async () => {
jest.clearAllMocks();
await mockedSequelize.close();
})
test('1# Register user', async () => {
const userService = new UserAccountService()
jest.spyOn(UserModel, 'create')
const registerRequest: RegisterRequest = {
email: '[email protected]',
password: '1234'
}
const registeredUser = await userService.register(registerRequest);
expect(UserModel.create).toHaveBeenCalledTimes(1);
expect(registeredUser).toBeInstanceOf(UserModel);
expect(registeredUser.email).toEqual('[email protected]')
});
});
This might throw an error, sqlite package is not found. You can install sqlite3
package and then start testing.
Upvotes: 0