davinkevin / AngularStompDK

Angular service to communicate to Stomp-Websocket
http://davinkevin.github.io/AngularStompDK/
Apache License 2.0
36 stars 12 forks source link

ngStomp service mock #51

Closed ioana-m closed 7 years ago

ioana-m commented 7 years ago

Could you please help with some unit test example, on how to mock the ngStomp service.

Thank you.

davinkevin commented 7 years ago

You can mock the ngStomp as any other service in the angularJs world.

The only different element is you have to deal with is the fluent API... which is more verbose to mock.

Could you post an example of a (simple) component you want to test to help you with this ?

BTW, I originally want to provide mock specially for ngStomp (https://github.com/davinkevin/AngularStompDK/tree/master/mock) but I never had others methods... If you want to improve it, PR are welcomed

ioana-m commented 7 years ago

I wanted to mock ngStomp service and only enter the callback function. But could mock only all methods by callFake, so at the moment the real callback function is not called. Here is how I mocked the service

`

        beforeEach(module(function($provide)` {
    $provide.service('ngstomp',` function() {
        var deferred;
        this.subscribeTo = jasmine.createSpy('subscribeTo').and.callFake(function(message) {
            return this;
        });
        this.callback = jasmine.createSpy('callback').and.callFake(function(message) {
            return this;
        });
        this.connect = jasmine.createSpy('connect').and.callFake(function(message) {});
        this.send = jasmine.createSpy('send').and.callFake(function(message) {});
        this.settings = jasmine.createSpy('settings').and.returnValue({headers : {}});
    });
}));

`

The only other way I am thinking now is to actually call the my callback function in the unit test in order to test the expected results.

davinkevin commented 7 years ago

I would like to have an example of your code, not your test.

If I take an example coming from the documentation :

angular.controller('myController', function($scope, ngstomp) {

    var items = [];

    ngstomp
        .subscribeTo('/topic/item')
            .callback(whatToDoWhenMessageComming)
        .connect()

    function whatToDoWhenMessageComming(message) {
        items.push(JSON.parse(message.body));
    }
 });

What you want to check is if a callback was registered with ngstomp ? You can do that by mocking ngStomp on subscribeTo method and return a real SubscribeBuilder.

let mockNgStomp = jasmine.createSpyObj('ngstomp', ['subscribeTo']);
mockNgStomp.subscribeTo.and.callFake((path) => new SubscribeBuilder(mockNgStomp, path));

With that, you can access the callback and all the parameter defined during subscription of your app.

ioana-m commented 7 years ago

In the example above I want to test that the items are the ones expected. So I want to test method whatToDoWhenMessageComming, if it receiving a mock message, parses correctly the data and sets it to the scope (my function whatToDoWhenMessageComming has a lot more logic)

ioana-m commented 7 years ago

Here is an example of my current code. In controller: service.serviceMethod($rootScope.param1, $rootScope.param2);

In service: `

function serviceMethod(param1, param2) {
            unSubscriber =

                ngstomp
                    .subscribeTo('...')
                    .callback(function(message) {
                        processMessage(message, param1, param2);
                    })
                    .connect();
        }`

I want to test the processMessage method. I can call her separately in unit test with the proper params, but I wanted to also test it included in the controller test.

davinkevin commented 7 years ago

In unit testing, you test only one part of the app. In the angular world, we separate testing of controller and service.

So in your code example, I will write two test, one for the controller with a mock service.serviceMethod where I only check if the method is called with the right parameter.

And, I will write another test for the service where I will mock the ngstomp service and will use the strategy describe in my last message (returning SubscribeBuilder where I can get the callback and call it). At then end, I will check if the method is called with the right parameters...

I will try to provide you an example for this, but right now, I'm writing this answer with my phone, so this is a little bit tricky to write code 😄

ioana-m commented 7 years ago

Hello, Could you please help once more and give me the examples you are referring in the previous message. Thank you.

davinkevin commented 7 years ago

I don't have any project setup to write real test like you want, so maybe my example will contain error.

I'm writing it in es2015 for simplicity

export class AService {

    constructor(ngstomp) {
        this.ngstomp = ngstomp;
    }

    serviceMethod(param1, param2) {
        unSubscriber = ngstomp
            .subscribeTo('/path')
            .callback(m => processMessage(m, param1, param2))
            .connect();
    }

    processMessage(a, b c){
        ...
    }

}

Like I said, I would like to test two things :

I will check if the method AService.serviceMethod trigger the connection and in another test if the call to processMessage trigger... what it should trigger.


describe(() => {
    let ngStompMock, service;

    beforeEach(() => {
        ngStompMock = jasmine.createSpyObj('ngStompMock', ['subscribeTo', 'callback', 'connect'])
        ngStompMock.subscribeTo.and.returnValue(ngStompMock);
        ngStompMock.callback.and.returnValue(ngStompMock);
        ngStompMock.connect.and.returnValue({});

        service = new AService(ngStompMock);
    });

    it('should register on the good url with a callback and connect', () => {

        /* When */
        service.serviceMethod('foo', 'bar');

        /* Then */
        expect(ngStompMock.subscribeTo).toHaveBeenCalledWith('/path');
        expect(ngStompMock.callback).toHaveBeenCalled());
        expect(ngStompMock.connect).toHaveBeenCalled());
    })

    it('should have the right callback when message come from ws', () => {
        /* Given */
        service.serviceMethod('foo', 'bar');
        let messageFromWS = "a stomp message you would like to test !"

        /* When */
        let callbackFunction = ngStompMock.callback.calls.argsFor(0)
        callbackFunction(messageFromWS)

        /* Then */
        expect(...).toBe(...);
        // Write your custom expectation around the logic triggerd by the processMessage function
    })

})

This is my point of view about testing this kind of things. We can do better by importing SubscribeBuilder, but this one is simple enougth I think.

Sorry for the delay, I see your message in my inbox since a long time, but I didn't find time to write an answer like this before... but I didn't forget it :D

Hope this help

:smile:

ioana-m commented 7 years ago

Thank you for the examples above.