Giter VIP home page Giter VIP logo

Comments (14)

santiq avatar santiq commented on May 16, 2024 3

Hi @lineldcosta, I'm glad to help you.

I totally forget to add tests examples to this repository, maybe next week I'll add a few examples.

For what I see in your code, you shouldn't call the containers in your tests, but mock every dependency of your service class.

Because one of the benefits of using Dependency Injection is to be able to use any implementation of the dependency as long as you don't change the interfaces and response types.

See this example:

Here we have a simple service class that handles user signup, so it creates a user in the database and also returns an access token like a JWT.

src/services/auth.ts

class AuthService {
constructor( @Inject('userModel') private userModel )

public SignUp(userObject) {
  const userRecord =  this.userModel.create(userObject)
  return { userRecord, JWT: '12345678.1234578.2134578' };
}
}

A good unit-test should only test the logic of the Service Unit, without calling the actual database. (that would be an integration test or even an end-to-end test)

I'm using mocks and spies from Jest but I'm sure you can find something equivalent in other tests frameworks.

tests/unit/services/auth

import UserService from '../../../src/services/auth';

describe('User Service Unit tests', () => {

  describe('Signup ', () => {
   const  userObject = { email: '[email protected]', password: '12345678' };

   const userModelMock = {
     create: jest.fn().mockReturnValue(userObject) // <- Important, I'm creating a mock of the method that we know will be called. And also, mocking the response.
   }

   const userServiceInstance = new UserService(userModelMock);

   const result = userServiceInstance.SignUp(userObject);

   expect(result.user.email).toBeDefined();
   expect(result.user.password).toBeDefined();
   expect(result.jwt).toBeDefined();
  
    // Assert that the underlaying dependencie was called
   expect(userModelMock.create).toBeCalled();  
  })

The reason to not test the create method from the model, it's because the library already has those tests, you shouldn't worry about them!

What do you think?

from bulletproof-nodejs.

lineldcosta avatar lineldcosta commented on May 16, 2024 2

Thank you boss!!!! i'm happy!

from bulletproof-nodejs.

lineldcosta avatar lineldcosta commented on May 16, 2024 1

Hey as per your instructions, i have changed the code, its working can you just check services folder and core(models) folder once and please let me know anything i need to update.

Below is the git ink
https://github.com/lineldcosta/sample-nodejs

from bulletproof-nodejs.

lineldcosta avatar lineldcosta commented on May 16, 2024 1

Hi,
Sorry to disturb you again! :-)

I am facing some difficulties in integrating unit-testing for services folder(error : TypeError: this.db.query is not a function)
I have added integration test. But unit testing for services folder, not getting enough idea how to implement it.
I have added the repo https://github.com/lineldcosta/sample-node-testing.git
Can you please give some idea for unit testing services and models
services folder: /tests/services/status/index.spec.ts
models folder: /tests/core/status/index.spec.ts

from bulletproof-nodejs.

santiq avatar santiq commented on May 16, 2024

I think it is because of how you are exporting your model.

You are instantiating the Status class at the moment of export export default new Status() so @Inject cannot be executed because it's only called when the service is decorated with @service and instantiated through a service locator.

So, you can refactor your code in this way

import { Service, Inject, Container } from 'typedi';
import { Pool } from 'pg';
@Service() // notice this change
class Status {

  constructor(
     @Inject('db') private db1;
) {
  }

  public async getApiStatus(): Promise<string> {
    console.log(this.db1);
    let pool: Pool = this.db1; 

    return new Promise<string>((resolve, reject) => {
      pool.query('select * from status;', (error, results) => {
        if (error) {
          throw error;
        }
        console.log(results.rows);
      });

      resolve('success');
    });
  }
}

export default Status; // notice this change

And then to get an instance of the Status class use

import { Container } from 'typedi';
import Status from '../models/status';

const statusInstance = Container.get(Status); 

from bulletproof-nodejs.

lineldcosta avatar lineldcosta commented on May 16, 2024

Thanks for the update.

i am using below service to use the above models as mentioned in the bulletproof-nodejs.

// path-to-folder/services/status/index.ts

import { Service, Inject } from 'typedi';

@Service()
export default class StatusService {
  constructor(@Inject('statusModel') private statusModel) {}
  public async status() {
    try {
      let apiStatus = await this.statusModel.getApiStatus();
      //throw 'error occured';
      return {
        apiStatus: apiStatus,
      };
    } catch (e) {
      throw e;
    }
  }
}

In this case it will throw the error TypeError: this.statusModel.getApiStatus is not a function

but you have used mongoose database and works fine!

from bulletproof-nodejs.

santiq avatar santiq commented on May 16, 2024

I think is not related to the database you choose.

How are you instantiating the StatusService ?

Do you use the service locator?
Container.get('statusService')

Or do you use the class constructor?
new StatusService( new StatusModel( new DatabaseConnection() ) )

Also, notice that in your example, you are using @Inject('statusModel') but you need to declare that string 'statusModel' to be the id of the class to instantiate otherwise the dependency injector will not find it.

import { Service, Inject, Container } from 'typedi';
import { Pool } from 'pg';
@Service('statusModel') // notice this change, now the service has a name
class Status {

  constructor(
     @Inject('db') private db1;
) {
  }

  public async getApiStatus(): Promise<string> {

from bulletproof-nodejs.

santiq avatar santiq commented on May 16, 2024

@lineldcosta Seems pretty good!

from bulletproof-nodejs.

lineldcosta avatar lineldcosta commented on May 16, 2024

Looks cool!

I understand the concept now :-)
I think i was doing integration test for every module till now.
Thanks for the answer. I will try to integrate the concept you explained.

from bulletproof-nodejs.

pprathameshmore avatar pprathameshmore commented on May 16, 2024

How to test chained mongoose functions?

 try {
            return await this.assetModel
                .find(filter)
                .populate('owner')
                .sort(sort)
                .limit(limit)
                .skip(page);
        } catch (error) {
            throw error;
        }

from bulletproof-nodejs.

santiq avatar santiq commented on May 16, 2024

@pprathameshmore
You shouldn't test a library, or third-party code, those are supposed to have their own test coverage.

from bulletproof-nodejs.

pprathameshmore avatar pprathameshmore commented on May 16, 2024

I am trying to test using your approach, for single-function it works fine. But when trying to test this function. I am getting this error.

This is my code:

   test('should return assets collection', async () => {
        const assetsReturnedMockValue = [
            {
                type: 'Application',
                id: '5fedc45308510741a40f0934',
                assetId: 'SLKTM123',
                name: 'Application',
                deleted: false,
                risk: 9,
                owner: null,
            },
            {
                type: 'Application',
                id: '5fedc45308510741a40f0934',
                assetId: 'SLKTM123',
                name: 'Application',
                deleted: false,
                risk: 9,
                owner: null,
            },
        ];

        const assetModelMock = {
            find: jest.fn().mockReturnValue(assetsReturnedMockValue),
        };

        const assetServiceInstance = new AssetServices(
            assetModelMock,
            assetModelMock
        );

        const result = await assetServiceInstance.getAssets();

        expect(result).toBeDefined();
        //Assert that the underlying dependencies was called
        expect(assetModelMock.find).toBeCalled();
    });

Error:
image

from bulletproof-nodejs.

pprathameshmore avatar pprathameshmore commented on May 16, 2024

Whenever try to test only output, getting the above error

from bulletproof-nodejs.

pprathameshmore avatar pprathameshmore commented on May 16, 2024

@santiq How to test dependent Services?

image

from bulletproof-nodejs.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.