PrivateSky / swarmcore

Swarm 2.0 implementation
Other
15 stars 4 forks source link

Mocking Adapters for testing #10

Open jwulf opened 9 years ago

jwulf commented 9 years ago

At the moment I'm writing unit tests for my adapter code.

I'm using mockery to inject a mock swarmcore module. I've created a MockAdapter class and I'm subclassing this for each adapter.

At the moment I have to export each adapter method from the adapter module, and then inject them explicitly in the mock adapter.

Is there any easy way to do this atm that I am missing?

jwulf commented 9 years ago

At the moment I'm doing it like this:

MockAdapter.js:

var mockery= require('mockery');
mockery.enable({warnOnUnregistered: false});    

var swarmcoreMock = {
    createAdapter: function (name) {
    console.log('Swarmcore mock creating adapter ' + name);
    }   
};

mockery.registerMock('swarmcore', swarmcoreMock);

/* This is a mockAdapter for testing. */

// Constructor
function MockAdapter(id) {
    this.swarmDetails = {
        swarmFile: '',
        phase: '',
        args: []
    };
    this.swarmTestCallback = null;
}

MockAdapter.prototype.startSwarm = function(){
    this.swarmDetails = {
        swarmFile: '',
        phase: '',
        args: []
    };

    for (var i = 0; i < arguments.length; i++) {
        if (i === 0) this.swarmDetails.swarmFile = arguments[0];
        if (i === 1) this.swarmDetails.phase = arguments[1];
        if (i > 1) this.swarmDetails.args.push(arguments[i]);
  }
}

module.exports = MockAdapter; 
  1. In my test spec I create a new MockAdapter
  2. Then I require the adapter I want to test.
  3. Since mockery is enabled, the swarmcore module in the adapter is mocked.
  4. In my adapter I export the adapter functions using module.exports
  5. I inject the functions from the adapter into my MockAdapter
  6. I run the unit tests.
  7. I can inspect the MockAdapter to see if a swarm has been started by my unit test.

Here is an example test, for a mail gateway adapter that starts a swarm in response to particular email addresses:

MailAdapter.spec.js:

var adapter = new MockMailAdapter();

describe("MailAdapter MessageHandler", function() {

    beforeEach(function(done){
        adapter.startSwarm('',''); // reinitialize our swarmer
        setTimeout(function(){
            done();
        }, 1000);
    });

    it("correctly routes a new customer in consumer mode", function(done) {     
        msg = {envelopeTo: [{address:'newcustomer-consumer@mailrobot.com'}],
            to: [{address: 'testclient@testdomain.com'}]};

        adapter.mailMessageHandler(null, msg);

            setTimeout(function() {
            expect(adapter.swarmDetails.swarmFile).toEqual("NewCustomerSwarm.js");
            expect(adapter.swarmDetails.args.length).toEqual(1);
            expect(adapter.swarmDetails.args[0]).toEqual('testclient@testdomain.com');
            expect(adapter.swarmDetails.phase).toEqual('consumerMode');
            done();
        }, 3000);
}); 
jwulf commented 9 years ago

I've changed it further, I'll submit a pull request.

salboaie commented 9 years ago

I will merge your proposal, as it does not harm anything but I will ask you a stupid question. I suppose you want to create tests for exported functions in adapters. Why you do not create a normal js module and make global functions in adapters from functions exported by that module?

Many adapters can become complex and get hard dependencies by data bases or other systems that you can't generally provide by a generic mocking like one proposed by you.

What do you think?

On Sun, Jul 12, 2015 at 7:26 PM, Sitapati notifications@github.com wrote:

I've changed it further, I'll submit a pull request.

— Reply to this email directly or view it on GitHub https://github.com/salboaie/SwarmCore/issues/10#issuecomment-120738607.

Sînică Alboaie

jwulf commented 9 years ago

Hmm.... my approach is naive because I haven't written many adapters and am still coming to grips with the architecture.

Some more thoughts on this over in https://github.com/salboaie/SwarmESB/issues/12

jwulf commented 9 years ago

I think decoupling the code, like the decoupled nature of the ESB itself, leads to greater reuse and sharing. Better ecosystem for innovation.

Dependency injection is a good pattern for decoupling components. Then the hard dependencies can be injected into generic, reusable code.

Having that as a coding style, and an opinionated set of tools and formats that support it, will fulfil on:

it would be desirable that all ESBs developed around swarm communication idea to get identical APIs and compatible programming styles.

salboaie commented 9 years ago

Yes, it is great that you ask questions and propose great things to improve SwarmESB''s code quality. Keep pushing me, it is a very good thing :)

Of course, I will not have the energy to change everything to an ideal form or I could have my own opinions (sometimes non standard approaches, even wired one but usually I have a point and I'm trying to explore more on some ares)

I'm not sure if you got a chance to look on my approach on dependency injection... I'm refactoring things step by step to use DI containers in adapters and sometimes in core.

I've create a monstrous node,js module (should be divided in 3-4 modules) https://github.com/salboaie/semantic-firewall

Among other things, it contains a DI container.. (it seems that it lacks any documentations for now...)

But... look a bit on the basic usage: https://github.com/salboaie/semantic-firewall/blob/master/test/diBasic.js

It is fairly similar with angular.js dependency injection, but with something important for swarm stability: I want to know when a service become unavailable in order to take measures when this happens. As far as I know, no other DI container has this feature.

What do you think about it?

On Mon, Jul 13, 2015 at 5:14 PM, Sitapati notifications@github.com wrote:

I think decoupling the code, like the decoupled nature of the ESB itself, leads to greater reuse and sharing. Better ecosystem for innovation.

Dependency injection is a good pattern for decoupling components. Then the hard dependencies can be injected into generic, reusable code.

Having that as a coding style, and an opinionated set of tools and formats that support it, will fulfil on:

it would be desirable that all ESBs developed around swarm communication idea to get identical APIs and compatible programming styles.

— Reply to this email directly or view it on GitHub https://github.com/salboaie/SwarmCore/issues/10#issuecomment-120941585.

Sînică Alboaie

jwulf commented 9 years ago

Yes, I did notice semantic-firewall and that it has DI.

I'm thinking about a way to inject magic strings into adapters. Could this be used for that?

I was also thinking of having a configuration adapter, and having adapters request their config from it via the bus when they start.

salboaie commented 9 years ago

Can you give an example about injecting magic strings in adapters?

About serving configurations from bus, not sure. Probably you noticed SWARM_CONFIG environment variables and adapters can take another config file so you can keep in in git. I never had any other need but don't know :) Give me more details about the case you want to solve...

On Tue, Jul 14, 2015 at 12:33 AM, Sitapati notifications@github.com wrote:

Yes, I did notice semantic-firewall and that it has DI.

I'm thinking about a way to inject magic strings into adapters. Could this be used for that?

I was also thinking of having a configuration adapter, and having adapters request their config from it via the bus when they start.

— Reply to this email directly or view it on GitHub https://github.com/salboaie/SwarmCore/issues/10#issuecomment-121066572.

Sînică Alboaie