Giter Club home page Giter Club logo

Comments (26)

unscriptable avatar unscriptable commented on May 17, 2024

Hey @n0rad, I took a look at your requirejs-isolate repo and can't fully understand the requirements from just browsing code. Do you have any documentation? Thanks! -- John

from curl.

n0rad avatar n0rad commented on May 17, 2024

Hi @unscriptable

Here is what I want to do, maybe you can find me an existing solution :

define(['a', 'js!b.js', 'text!c.html', 'css!d.css'], function(...) {
...
});

During UNIT test process I want to require my module with real c and d
but I want a and b to be stubs.

Can I do that with curl ?

from curl.

unscriptable avatar unscriptable commented on May 17, 2024

Hey @n0rad!

There are a few ways to do this now. Are you using a test framework?

The simplest of which is to just define the mocks/stubs in the test harness:

// this code would be inside your test harness
// define stubs for unit-under-test
// unit-under-test: define(['a', 'js!b.js', 'text!c.html', 'css!d.css'], function() {});

// stub for a. you could insert spies here
define('a', function () {
    return {
        stubMethod: function (val) { return val; },
        anotherStub: function (a, b, c) { return c; }
    };
});

// stub for b.js
define('js!b.js', function () {
    return {
        something: function (val) { return val; },
        useful: function (a) { return a; }
    };
});

// laod and run test here
// c.html and d.css will be loaded from server as usual
curl(['unit-under-test'], function (unitUnderTest) {
    // add tests here
});

Here's another way to do it:

curl({
    paths: {
        a: 'path/to/stub/a',
        'js!b.js': 'path/to/stub/b.js'
    }
});

curl(['unit-under-test'], function (unitUnderTest) {
    // tests here
});

In general, I prefer using packages over paths, but this is a good use case for paths. :)

Does this help?

-- John

from curl.

n0rad avatar n0rad commented on May 17, 2024

Hi @unscriptable

The problem is that I'm running all my tests in the same environment (I'm writing a js test plugin for maven supporting curl.js) and curl do not accept multiple define with the same name.

Therefore I cannot mock 'a' with 2 different ways in 2 different tests

Do you have a solution for that ?

from curl.

unscriptable avatar unscriptable commented on May 17, 2024

I can think of a few ways to do this. What do you think of these?

  1. Add contexts to curl. This will likely take a bit of effort. It's related to work I was planning to do for the 0.7 release, but won't be getting around to for a few more weeks.
  2. Create a shim module similar to curl/debug that only adds the curl.undefine (or possibly define.undefine) function. The curl/debug module already adds an undefine method to the curl global. We could also easily add an option to undefine everything quite easily.
  3. Add a config option to allow module re-definition by default. This seems dangerous to me. :)
  4. Create a package loader module that declares all resources as "dynamic". This is a reasonable alternative to 2.

I'm leaning towards option 2 since it'd be very easy for me to create.

What do you think?

-- J

from curl.

unscriptable avatar unscriptable commented on May 17, 2024

Sorry, I didn't have time to test it this morning, but feel free to try this if you find some time:

https://github.com/cujojs/curl/blob/undefine/src/curl/shim/undefine.js

It's a manifestation of option 2.

You have to ensure that the undefine module is loaded before you define any stubs. Using the preload config option is an easy way to do that.

curl.undefine(true); // removes all modules from the cache
curl.undefine('stub1'); // removes only stub1 from the cache
curl.undefine(['stub1', stub2', stub3']); // removes all three modules from the cache

-- John

from curl.

n0rad avatar n0rad commented on May 17, 2024

Here is my response about options:

  1. context should be great, I will be able to create a context for each test file.
  2. tested and approved. I will use it by resetting all the context at the beginning of each test files (and I will change for option 1 when 0.7 will be released).
  3. I think its dangerous too.
  4. I don't really understand how this would work. Is this package loader will allow redefinition for his scope ?

The problem is that I will reload real implementation for each test file. This is not a problem for the moment but with a very large number of tests it could become a problem, but I don't know how we could change this.

from curl.

n0rad avatar n0rad commented on May 17, 2024

In fact its not working,

As I have only one context and modules are loaded asynchronously, stub definitions override themselves.

from curl.

unscriptable avatar unscriptable commented on May 17, 2024

Can you post some code somewhere so I can see what you're seeing? Thanks! -- J

from curl.

n0rad avatar n0rad commented on May 17, 2024

I'have create a repo : https://github.com/n0rad/curl-test (you can commit on it)

Both tests should be green. You can see that by reversing tests require in index.html the test in green change.

It should be a problem with concurrent modification of the context but I don't know how I can solve this without creating all the curl context in the test himself (which would be too verbose).

Oh and I Really want to thank you for your time and your incredible libs, you and Brian.

from curl.

unscriptable avatar unscriptable commented on May 17, 2024

Wow. Thanks for a whole friggin repo! :) That's awesome. I'll try to take a look during lunch today or in the evening (east coast US). -- J

from curl.

unscriptable avatar unscriptable commented on May 17, 2024

Hey @n0rad,

This is the best solution I have so far: https://github.com/n0rad/curl-test/blob/curl-sequence/index.html

It sequences the fetching of the test modules so they don't clobber each others' mocks (inline, named defines).

There's another branch, "nested-require", that does something similar in an AMD-compliant, but messy way.

Does Jasmine support async tests? If so, we could defer the call to fetch the 'visuwall/command/wallCommand' module until we're inside the test function. This wouldn't force us to sequence the loading of the test modules.

define(function () {
    describe('wallCommandTest with delegate', function() {
        it('should delegate to controller to show wall', function(done) {
            // clear out modules and create stubs/mocks
            curl.undefine(true);
            var showWall = jasmine.createSpy('wallController.showWall');
            define('visuwall/controller/wallFormController', function() {});
            define('visuwall/controller/wallController', function() {
                return {showWall : showWall};
            });
            // get module now
            require(['visuwall/command/wallCommand'], function(wallCommand) {
                wallCommand.run(null, 'mywallname');
                expect(showWall).toHaveBeenCalledWith('mywallname');
                done(); // this is how buster.js does it
            });
        });
    });
});

Never mind. This code snippet has the same problem.

-- J

from curl.

unscriptable avatar unscriptable commented on May 17, 2024

Now I understand why you were looking for a plugin. :)

Here's what I am thinking:

  1. create an isolate! plugin that allows mocks or stubs to be substituted for dependencies of a module
  2. allow the plugin to be configured with a mapping of modules-under-test to mocks/stubs:
    curl({
        plugins: {
            isolate: {
                map: isolateMap
            }
        },
        baseUrl: 'blah',
        packages: { /* etc */ }
    });

where isolateMap is one of the following:

    isolateMap = {
        "visuwall/command": {
            "visuwall/controller/wallFormController": "mocks/visuwall/controller/wallFormController"
        }
    };

    isolateMap = {
        "visuwall/command": function (uutModuleId, depModuleId) {
            return aMockOrStub;
        }
    };

    isolateMap = function (uutModuleId, depModuleId) {
        return aMockOrStub;
    };

This will take a few days. :)

-- J

from curl.

n0rad avatar n0rad commented on May 17, 2024

I don't really see how it will work, Will It only implies the test file ?

Can you show me an simple example of what will be the declaration of the test and the source ?

from curl.

unscriptable avatar unscriptable commented on May 17, 2024

That isolateMap stuff did look unwieldy. What about something like this instead?

// this is a test module
define(['curl/tdd/createContext', 'require'], function (createContext, require) {

    var context = createContext(require);

    // configure test
    context.config({
        moduleId: 'pkg/moduleToTest',
        setup: function (require, define) {
            // define all of your mocks and stubs
            define('mock1', function () {});
            // or fetch them from the server
            require(['mock2', 'mock3']);
            // or fetch other resources needed for testing
            require(['supportModule'], function (support) {
                define('mock4', function () { return support.foo(42); });
            });
        },
        run: function (moduleToTest, doneCallback) {
            // insert tests here
            describe('wallCommandTest with delegate', function () {
                it('should do something', function () {
                    // do the asserts and such here with moduleToTest
                });
            })
        }
    });

    // call run()
    context.run();
});

This could also be written like this:

define(['curl/tdd/createContext', 'require'], function (createContext, require) {

    createContext(require)
        .config({ moduleId: 'pkg/moduleToTest', setup: setup, run: run })
        .run();

    function setup (require, define) {
        // define all of your mocks and stubs
        define('mock1', function () {});
        // or fetch them from the server
        require(['mock2', 'mock3']);
        // or fetch other resources needed for testing
        require(['supportModule'], function (support) {
            define('mock4', function () { return support.foo(42); });
        });
    }

    function run (moduleToTest, doneCallback) {
        // insert tests here
        describe('wallCommandTest with delegate', function () {
            it('should do something', function () {
                // do the asserts and such here with moduleToTest
            });
        })
    }
});

curl/tdd/createContext is a new module I created just now. The code above follows its API.

Would this work? If so, I'll create some tests and push it to the dev branch.

-- J

from curl.

n0rad avatar n0rad commented on May 17, 2024

It should work.

Just one remark the run parameters / moduleId may not be only one module but maybe a list of modules.

from curl.

unscriptable avatar unscriptable commented on May 17, 2024

Ok. How does this look?

// this is a test module
define(['curl/tdd/createContext', 'require'], function (createContext, require) {

    // supply a require (optional) if your module ids are relative
    var context = createContext(require);

    context.setup(function (require, define /*, complete*/) {
        // define all of your mocks and stubs
        define('mock1', function () {});
        // or fetch them from the server
        require(['mock2', 'mock3']);
        // or fetch other resources needed for testing
        require(['supportModule'], function (support) {
            define('mock4', function () { return support.foo(42); });
            // complete is redundant here, see comments
            /*complete();*/
        });
    }).cleanup(function (undefine /*, complete*/) {
        // undefine any modules that may have been loaded via deep
        // dependencies
        undefine('module-used-by-moduleToTest');
        // complete is not necessary here, see comments
        /*complete();*/
    }).run(function (require, define, complete) {
        // first module (or modules) to test
        require(['moduleToTest'], function (moduleToTest) {
            // insert tests here
            describe('something', function () {
                it('should do something', function () {
                    // do the asserts and such here with moduleToTest...

                    // if this code is async, you must call complete
                    complete();
                });
            });
        });
    }).run(function (require, define, complete) {
        // second module (or modules) to test
        require(['moduleToTest2'], function (moduleToTest2) {
            // insert tests here
            describe('something else', function () {
                it('should do something else', function () {
                    // do the asserts and such here with moduleToTest

                    // if this code is async, you must call complete
                    complete();
                });
            });
        });
    });

});

If the code in any of the setup(), cleanup(), or run() functions is introducing async behavior, then you should include the third parameter (complete). Call complete() when the test is completed. If the code is async because it uses require(), there is no need to call complete() since the requires are already tracked by the context.

The setup() function is called for each run() so modules defined or required in setup() are released and recreated for each run(). After each run(), all modules loaded directly by the supplied require() are cleaned up / released. You can clean up any other resources between run()s by supplying code in the cleanup() function. For instance, if there are any nested dependencies that you want to clean up, you can do it here.

I can have this done by the weekend if this fits your needs.

-- J

from curl.

n0rad avatar n0rad commented on May 17, 2024

Perfect :)

from curl.

n0rad avatar n0rad commented on May 17, 2024

Did you had time to work on it ?

from curl.

unscriptable avatar unscriptable commented on May 17, 2024

Hi @n0rad!

Check out https://github.com/cujojs/curl/blob/tdd/src/curl/tdd/runner.js

There are a few tests here: https://github.com/cujojs/curl/blob/tdd/test/tdd/runner.html

I changed the interface a bit:

var run1 = runner(require, setup, teardown);

run1.run(testingFunction).then(doSomethingElse);

setup, teardown, and testingFunc all have the same parameters:

function (require, done) {
    // for setup, configurations go here
    // for testing, assertions go here
    // for teardown, clean ups go here
}

Only include the done parameter if your function will perform async tasks (other than async require calls). Do not include the done parameter otherwise!

Try it out when you get a chance and let me know your thoughts. Was it better when the run() functions were chainable?

run1.run(test1).run(test2).run(test3);

Thanks!

-- John

from curl.

n0rad avatar n0rad commented on May 17, 2024

I updated https://github.com/n0rad/curl-test with runner but its still not work

maybe I missed something

1- on loading 2 tests it still do not work, it tries to load real implementation but with only 1 its working
2- as the definition is async I added an hack in then() to know when everything is loaded and then run tests but i don't know if its the good way to go.

Can you have a look on why its trying to load real implementations when 2 tests are loaded.

Thanks

from curl.

unscriptable avatar unscriptable commented on May 17, 2024

Sorry. I forgot you had a test repo. I'll look at that now. -- J

from curl.

unscriptable avatar unscriptable commented on May 17, 2024

Ok. I see two small issues. I should be able to fix those by tomorrow morning. Thanks for testing this. -- J

from curl.

unscriptable avatar unscriptable commented on May 17, 2024

Hey @n0rad, try it now. The enqueue function was broken. -- J

from curl.

n0rad avatar n0rad commented on May 17, 2024

Great, I will start using it on a real project to be sure nothing is missing

from curl.

unscriptable avatar unscriptable commented on May 17, 2024

I fixed a bug when using the done parameter, and then I merged the entire tdd branch into the dev branch. FYI -- John

from curl.

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.