NestJs - Jest - Testing: ConnectionNotFoundError: Connection "default" was not found - testing

I am trying to write a test of a simple TODO App.
This is my service test class:
const mockTaskRepository = () => ({
createTask: jest.fn(),
});
describe('TasksService', () => {
let tasksService;
let taskRepository;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [TasksService, { provide: TaskRepository, useFactory: mockTaskRepository }],
}).compile();
tasksService = await module.get<TasksService>(TasksService);
taskRepository = await module.get<TaskRepository>(TaskRepository);
});
describe('create task', () => {
it('calls taskRepository.create() and returns the result', async () => {
const createTaskDto = {
title: 'Test task',
description: 'Test desc',
};
taskRepository.createTask.mockResolvedValue('someTask');
expect(taskRepository.createTask).not.toHaveBeenCalled();
const result = await tasksService.createTask(createTaskDto);
expect(taskRepository.createTask).toHaveBeenCalledWith(createTaskDto);
expect(result).toEqual('someValue');
});
});
});
And this is my task service:
#Injectable()
export class TasksService {
constructor(
#InjectRepository(TaskRepository)
private taskRepository: TaskRepository,
) {}
async createTask(createTaskDto: CreateTaskDto): Promise<Task> {
const { title, description } = createTaskDto;
const task = new Task();
task.title = title;
task.description = description;
task.status = TaskStatus.IN_PROGRESS;
await task.save();
return task;
}
}
When I try to run the Create a Task test, the error below occurs.
FAIL src/tasks/tasks.service.spec.ts
● TasksService › create task › calls taskRepository.create() and returns the result
ConnectionNotFoundError: Connection "default" was not found.
at new ConnectionNotFoundError (error/ConnectionNotFoundError.ts:8:9)
at ConnectionManager.Object.<anonymous>.ConnectionManager.get (connection/ConnectionManager.ts:40:19)
at Object.getConnection (index.ts:247:35)
at Function.Object.<anonymous>.BaseEntity.getRepository (repository/BaseEntity.ts:85:72)
at Task.Object.<anonymous>.BaseEntity.save (repository/BaseEntity.ts:50:42)
at TasksService.createTask (tasks/tasks.service.ts:35:14)
at Object.<anonymous> (tasks/tasks.service.spec.ts:69:38)
Test Suites: 1 failed, 1 passed, 2 total
Tests: 1 failed, 8 passed, 9 total
Anyone know what's the mistake in the code?
..........................................
Thanks in advance!

If it is looking for connection default then I assume that the TypeOrmModule configs are still being taken into account. Instead of provide: TaskRepository try changing it to provide: getRepositoryToken(TaskEntity) if you have a TaskEntity. This will tell Nest to override the default repo that the #InjectRepository() decorator tries to provide.
If that isn't the case, do you think you could add your TaskService and TaskModule classes as well?

You should mock the repository then bringing up the context of Test Module. Otherwise, the real Repository is being injected (via Nest's DI) into service.
const module = await Test.createTestingModule({
providers: [
{
provide: getRepositoryToken(YourEntityClass),
useValue: mockedRepo, // or use class
},
TasksService],
}).compile();
tasksService = await module.get<TasksService>(TasksService);
// taskRepository = await module.get<TaskRepository>(TaskRepository); don't have to do that if `useValue` was used instead of factory
So, TL;DR :
provide: TaskRepository should provide Token of given Injectable in this case: https://docs.nestjs.com/fundamentals/custom-providers#non-class-based-provider-tokens

The test expected method Repository.createTask to be called, but method Service.createTask didn't make that call.
The fix: Update the Service method to delegate to the Repository method the task creation.
// tasks.service.ts
async createTask(createTaskDto: CreateTaskDto, user: User): Promise<Task> {
return this.taskRepository.createTask(createTaskDto, user);
}
// tasks.repository.ts
async createTask(createTaskDto: CreateTaskDto, user: User): Promise<Task> {
const { title, description } = createTaskDto;
const task = new Task();
task.title = title;
task.user = user;
task.description = description;
task.status = TaskStatus.IN_PROGRESS;
await task.save();
delete task.user;
return task;
}
}

Related

How to update same fixture file within the same BeforEeach hook and get the updated details as the response in Cypress [duplicate]

I am trying to write a test with the new cypress 6 interceptor method (Cypress API Intercept). For the test I am writing I need to change the reponse of one endpoint after some action was performed.
Expectation:
I am calling cy.intercept again with another fixture and expect it to change all upcomming calls to reponse with this new fixture.
Actual Behaviour:
Cypress still response with the first fixture set for the call.
Test Data:
In a test project I have recreated the problem:
test.spec.js
describe('testing cypress', () => {
it("multiple responses", () => {
cy.intercept('http://localhost:4200/testcall', { fixture: 'example.json' });
// when visiting the page it makes one request to http://localhost:4200/testcall
cy.visit('http://localhost:4200');
cy.get('.output').should('contain.text', '111');
// now before the button is clicked and the call is made again
// cypress should change the response to the other fixture
cy.intercept('http://localhost:4200/testcall', { fixture: 'example2.json' });
cy.get('.button').click();
cy.get('.output').should('contain.text', '222');
});
});
example.json
{
"text": "111"
}
example2.json
{
"text": "222"
}
app.component.ts
import { HttpClient } from '#angular/common/http';
import { AfterViewInit, Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewInit {
public text: string;
public constructor(private httpClient: HttpClient) { }
public ngAfterViewInit(): void {
this.loadData();
}
public loadData(): void {
const loadDataSubscription = this.httpClient.get<any>('http://localhost:4200/testcall').subscribe(response => {
this.text = response.body;
loadDataSubscription.unsubscribe();
});
}
}
app.component.html
<button class="button" (click)="loadData()">click</button>
<p class="output" [innerHTML]="text"></p>
Slightly clumsy, but you can use one cy.intercept() with a Function routeHandler, and count the calls.
Something like,
let interceptCount = 0;
cy.intercept('http://localhost:4200/testcall', (req) => {
req.reply(res => {
if (interceptCount === 0 ) {
interceptCount += 1;
res.send({ fixture: 'example.json' })
} else {
res.send({ fixture: 'example2.json' })
}
});
});
Otherwise, everything looks good in your code so I guess over-riding an intercept is not a feature at this time.
As of Cypress v7.0.0 released 04/05/2021, cy.intercept() allows over-riding.
We introduced several breaking changes to cy.intercept().
Request handlers supplied to cy.intercept() are now matched starting with the most recently defined request interceptor. This allows users to override request handlers by calling cy.intercept() again.
So your example code above now works
cy.intercept('http://localhost:4200/testcall', { fixture: 'example.json' });
// when visiting the page it makes one request to http://localhost:4200/testcall
cy.visit('http://localhost:4200');
cy.get('.output').should('contain.text', '111');
// now cypress should change the response to the other fixture
cy.intercept('http://localhost:4200/testcall', { fixture: 'example2.json' });
cy.get('.button').click();
cy.get('.output').should('contain.text', '222');
Cypress command cy.intercept has the
times parameter that you can use to create intercepts that only are used N times. In your case it would be
cy.intercept('http://localhost:4200/testcall', {
fixture: 'example.json',
times: 1
});
...
cy.intercept('http://localhost:4200/testcall', {
fixture: 'example2.json',
times: 1
});
See the cy.intercept example in the Cypress recipes repo https://github.com/cypress-io/cypress-example-recipes#network-stubbing-and-spying
const requestsCache = {};
export function reIntercept(type: 'GET' | 'POST' | 'PUT' | 'DELETE', url, options: StaticResponse) {
requestsCache[type + url] = options;
cy.intercept(type, url, req => req.reply(res => {
console.log(url, ' => ', requestsCache[type + url].fixture);
return res.send(requestsCache[type + url]);
}));
}
Make sure to clean requestsCache when needed.

Create a document in beforeEach on Jest nest.js

I'm Using the in memory mongoose database for create my Unit test, and I want to create a document before the tests.
My interface is:
export interface IUsers {
readonly name: string;
readonly username: string;
readonly email: string;
readonly password: string;
}
and my beforeEach is:
import { MongooseModule } from "#nestjs/mongoose";
import { Test, TestingModule } from "#nestjs/testing";
import { closeInMongodConnection, rootMongooseTestModule } from '../test-utils/mongo/MongooseTestModule';
import { User, UserSchema } from "./schemas/users.schema";
import { UsersService } from "./users.service";
describe("UsersService", () => {
let service: UsersService;
let testingModule: TestingModule;
let userModel: Model<User>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
rootMongooseTestModule(),
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
],
providers: [UsersService],
}).compile();
service = module.get<UsersService>(UsersService);
//create user
userModel = testingModule.get<Model<User>>(
'UserModel',
);
});
I get an error TypeError: Cannot read pro perties of undefined (reading 'get') during the test. I tried to use let userModel: Model<IUsers>; But I get the same error.
Use either testingModule or module.
You declared testingModule but never initialized.
let testingModule: TestingModule; This part is undefined unless something is assigned to it.
Try like this
describe('UsersService', () => {
let testingModule: TestingModule;
let userModel: Model<User>;
let userService: UserService;
beforeEach(async () => {
testingModule = await Test.createTestingModule({
imports: [
rootMongooseTestModule,
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])],
providers: [UsersService],
}).compile();
userService = testingModule.get<UsersService>(UsersService);
userModel = testingModule.get<Model<User>>('UserModel');
// await userModel.create(...) or whatever methods you have
});
});

Invoking Testcafe 't' Handler Through Anonymous Self Executing Function

I have a case where I would like to use invoke the t test handler with the t.ctx context available outside of the test() function. Is this possible at all? From what I see when I run this script I do see the console spitting out all the available handler options to me, but trying to create an empty user form t.ctx.userForm = {} throws the error:
Cannot implicitly resolve the test run in the context of which the test controller action should be executed. Use test function's 't' argument instead.
import { t } from 'Testcafe';
const setFormContext = (t) => {
console.log(t);
t.ctx.userForm = {};
};
export const intializeEmptyForm = (async(t) => {
await setFormContext(t)
})(t);
I basically want to be able to have code like such, but without overcbloating the POM AccountPage object with custom functions not related to what's on the page, or relying on something like firstName to be invoked in order to make the t.ctx.userForm available.
export const AccountPage = {
enterFirstName: async (firstName) => {
let firstNameField = Selector('#firstName');
await t.typeText(firstNameField, firstName);
// t.ctx.userForm = {}; <- ideally not here as it's tied to enterFirstName
t.ctx.userForm.firstName = firstName;
},
enterLastName: async (lastName) => {
let lastNameField = Selector('#lastName');
await t.typeText(lastNameField, lastName);
t.ctx.userForm.lastName = lastName;
}
// ... some logic that maps t.ctx.userForm values to an assertion that checks all form values after clicking 'Save' are actually present.
}
import { AccountPage } from 'AccountPage';
...
test('User form successfully saves and updates correctly', async () => {
await AccountPage.enterFirstName('First');
await AccountPage.enterLastName('Last');
await AccountPage.clickSave()
})
The import {t} from 'testcafe' statement looks for a test(), beforeEach(), afterEach() or other test function in the call stack and gets the t instance from its arguments. This error occurs when an imported t is used in a function that is not called from a test or hook. This is what happens in your case, since the arrow function whose promise is exported in initializeEmptyForm is self-invoked.
As a solution, you can export a function in initializeEmptyForm (not a promise) and call it from test context.
helper.js
import { t } from 'testcafe';
export const initializeEmptyForm = async () => {
await setFormContext(t);
};
test.js
import { initializeEmptyForm } from './helper.js';
fixture 'fixture 1'
.beforeEach(async t => {
await initializeEmptyForm();
});
test('test 1', async t => {
// ...
});
Alternatively, you can export a function that takes t as an argument:
helper.js
export const initializeEmptyForm = async t => {
await setFormContext(t);
};
test.js
import { initializeEmptyForm } from './helper.js';
fixture 'fixture 1'
.beforeEach(async t => {
await initializeEmptyForm(t);
});
test('test 1', async t => {
// ...
});
My thoughts on this are when visiting a form with a click action then it may be cleaner to do so as part of the click action.
import { Selector, t } from 'testcafe';
export const AccountPage = {
clickEditForm: async () => {
let editButton = Selector('button').withText('Edit');
await t.click(editButton);
// guarantees on a click form we have setup the userForm object;
t.ctx.userForm = {};
},
enterFirstName: async (firstName) => {
let firstNameField = Selector('#firstName');
await t.typeText(firstNameField, firstName);
t.ctx.userForm.firstName = firstName;
},
enterLastName: async (lastName) => {
let lastNameField = Selector('#lastName');
await t.typeText(lastNameField, lastName);
t.ctx.userForm.lastName = lastName;
}
// map t.ctx.userForm values to assertions that checks all form values after clicking 'Save'.
verifyAccountFormDetails: async(expectFormValue = []) => {
// Grab form values
// Then map them to parameters desired or something.
}
}
This allows us to then pass the values around in a cleaner manner with the POM.
import { AccountPage } from 'AccountPage';
...
test('User form successfully saves and updates correctly', async () => {
await AccountPage.enterFirstName('First');
await AccountPage.enterLastName('Last');
await AccountPage.clickSave();
...
// After doing something like account form save then verify values
persist based on what you want to check
await AccountPage.verifyAccountFormDetails(['firstName', 'email'])
})

Nestjs pipe works when I manually create entity but not in jest test

I have a validation pipe to check input that works when I manually create a product(using postman), but it doesn't check when I run tests. any explanations?
my validator:
#Injectable()
export class JoiValidationPipe implements PipeTransform {
constructor(private schema: ObjectSchema) {}
transform(value: any, metadata: ArgumentMetadata) {
const { error } = this.schema.validate(value);
if (error) {
throw new HttpException('Validation failed', HttpStatus.BAD_REQUEST);
}
return value;
}
}
my controller:
#UsePipes(new JoiValidationPipe(productSchema))
#Post()
async create(#Body() createProductDto: CreateProductDto): Promise<Product> {
return (await this.productsService.create(createProductDto)).product;
}
my test:
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [ProductsController],
providers: [ProductsService],
}).compile();
controller = module.get<ProductsController>(ProductsController);
service = module.get<ProductsService>(ProductsService);
});
describe('create()', () => {
it('should fail to add a new product', async () => {
const result: Product = {
name: 'p',
price: -100,
category: 'junk',
};
expect(await controller.create(result)).toBe(result);
});
});
my schema:
export const productSchema: ObjectSchema = object({
createProductDto: object().keys({
name: string().min(5).required(),
price: number().integer().min(0).default(0),
category: string().min(5).required(),
}),
});
Pipes don't run unless you're going through the HTTP request. Same for other enhancers like guards and interceptors. If you want to test the pipe you can do that with supertest and e2e tests, or you can test the schema directly with joi in a different test suite

relay subscription onNext not triggered on react-native

I am a subscription setup but onNext is not getting triggered I am not sure why since this is my first time implementing subscription and docs was not much help with the issue.
Here are the code implementations:
import {
graphql,
requestSubscription
} from 'react-relay'
import environment from '../network';
const subscription = graphql`
subscription chatCreatedSubscription{
chatCreated{
id
initiate_time
update_time
support_id
category_id
email
name
}
}
`;
function chatCreated(callback) {
const variables = {};
requestSubscription(environment, {
subscription,
variables,
onNext: () => {
console.log("onNext");
callback()
},
updater: () => {
console.log("updater");
}
});
}
module.exports = chatCreated;
and here is my network for the subscription
import { Environment, Network, RecordSource, Store } from "relay-runtime";
import Expo from "expo";
import { SubscriptionClient } from "subscriptions-transport-ws";
import { WebSocketLink } from 'apollo-link-ws';
import { execute } from 'apollo-link';
import accessHelper from "../helper/accessToken";
const networkSubscriptions = async (operation, variables) => {
let token = await accessHelper();
if (token != null || token != undefined) {
const subscriptionClient = new SubscriptionClient("ws://localhost:3000/graphql",
{
reconnect: true,
connectionParams: {
Authorization: token,
},
});
execute(new WebSocketLink(subscriptionClient), {
query: operation.text,
variables,
});
}
}
const network = Network.create(fetchQuery, networkSubscriptions);
const store = new Store(new RecordSource());
const environment = new Environment({
network,
store
});
export default environment;
the subscription is called in a componentDidMount method on a component it executes but the onNext method inside the subscription is never triggered when new information is added to what the subscription is listening to.
so i figured out that my issue was the network js not being setup properly and the version of subscription-transport-ws. i added version 0.8.3 of the package and made the following changes to my network file:
const networkSubscriptions = async (config, variables, cacheConfig, observer) => {
const query = config.text;
let token = await accessHelper();
if (token != null || token != undefined) {
const subscriptionClient = new SubscriptionClient(`ws://${api}/graphql`,
{
reconnect: true,
connectionParams: {
Authorization: token,
},
});
subscriptionClient.subscribe({ query, variables }, (error, result) => {
observer.onNext({ data: result })
})
return {
dispose: subscriptionClient.unsubscribe
};
}
}
i hope this helps you if you get stuck with the same issue as mine.