Comments (26)
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.
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.
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.
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.
I can think of a few ways to do this. What do you think of these?
- 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.
- Create a shim module similar to curl/debug that only adds the
curl.undefine
(or possiblydefine.undefine
) function. The curl/debug module already adds anundefine
method to the curl global. We could also easily add an option to undefine everything quite easily. - Add a config option to allow module re-definition by default. This seems dangerous to me. :)
- 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.
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.
Here is my response about options:
- context should be great, I will be able to create a context for each test file.
- 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).
- I think its dangerous too.
- 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.
In fact its not working,
As I have only one context and modules are loaded asynchronously, stub definitions override themselves.
from curl.
Can you post some code somewhere so I can see what you're seeing? Thanks! -- J
from curl.
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.
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.
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 define
s).
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.
Now I understand why you were looking for a plugin. :)
Here's what I am thinking:
- create an isolate! plugin that allows mocks or stubs to be substituted for dependencies of a module
- 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.
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.
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.
It should work.
Just one remark the run parameters / moduleId may not be only one module but maybe a list of modules.
from curl.
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 require
s are already tracked by the context.
The setup()
function is called for each run()
so modules define
d or require
d 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.
Perfect :)
from curl.
Did you had time to work on it ?
from curl.
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.
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.
Sorry. I forgot you had a test repo. I'll look at that now. -- J
from curl.
Ok. I see two small issues. I should be able to fix those by tomorrow morning. Thanks for testing this. -- J
from curl.
Hey @n0rad, try it now. The enqueue function was broken. -- J
from curl.
Great, I will start using it on a real project to be sure nothing is missing
from curl.
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)
- curl-css shouldn't wait for readystate == 'complete' HOT 7
- What if the module is a function? HOT 1
- Similar functionality of curl with require.js map config? HOT 1
- Using domReady! as a dependency argument causes an error HOT 4
- dealing with the window object HOT 7
- Unhandled error is eaten by curl when bootstrap code has a json plugin dependency HOT 5
- For anyone facing the error : Multiple anonymous defines encountered HOT 1
- Multiple bundles HOT 17
- Remove old IE event listeners in domReady HOT 1
- License for curl HOT 4
- "undefined" from import when define function throws an exception HOT 8
- Modules "undefined" when error occurs HOT 1
- "new Promise()" syntax non-standard? HOT 1
- Promise.then doesn't wait HOT 5
- Add build with js plugin
- multiple anonymous defines HOT 2
- How to help curl guess the right plugin? (e.g. json! for *.json) HOT 8
- Feature Request: Add syntax to bypass plugins
- cjsm11 loader hides a TypeError and falsely reports success
- ε¦δΉ HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from curl.