Replacing Dependency Injection with NodeJS modules in tests
See original GitHub issueFirst breaking change announced! 🤩 🍰
Motivation
Currently to load support objects (I, page objects, etc) into tests we use dependency injection into a function. An object is loaded by its name declared in include
section of config file. So every test needs at least I
parameter of a function for a test:
Scenario('sample test', (I, loginPage, adminPage) => {
I.amOnPage('/');
// ...
});
However, the biggest limitation in DI that it works only for tests. Inside PageObjects and StepDefinitions (for BDD testing) we can’t rely on DI (or use absolutely a different API).
Solution
It appears that all support objects (except I
) can be loaded by simply requiring modules:
const I = actor(); // or require('codeceptjs).actor() if not globally installed
const loginPage = require('./loginPage');
Scenario('sample test', () => {
I.amOnPage();
});
Pros
By replacing DI loaded support objects with modules we:
- simplify configuration (
included
section in config is not needed) - make CodeceptJS friendlier to wider JS community
- unifying a way to load support objects
- allows to load objects from other Node modules, paths, globally, etc.
Cons
- Breaking change
- Requires manually (or semi-automatically) update to all tests
require
relies on relative path, so depending on a path you need to include an object differently.- It would be nice to have some kind of migration script to make this change https://www.npmjs.com/package/jscodeshift (additional work)
- There can be issues regarding async functions inside page objects. Right now we inject
recorder.saveFirstAsyncError
into every async function. https://github.com/Codeception/CodeceptJS/blob/master/lib/container.js#L182
Alternative Solution
An alternative with a shorter migration path is to provide global function inject
or support
, which will load all objects into test by their names. Unlike current approach, objects are loaded per file basis. And this can be used everywhere: in tests, in page objects, in step definitions:
const { I, loginPage, userPage } = inject();
Scenario('sample test', () => {
I.amOnPage();
});
Pros
- simpler code, shorter migration
- unified approach
- configuration is not changed
- does not use on relative paths to load objects
Cons
- requires an additional global object (
inject
) - magically loading modules
- higher learning curve for CodeceptJS
I really need a feedback to continue! Vote for solutions, propose an alternative. CodeceptJS 2.0 needs your input!
Issue Analytics
- State:
- Created 5 years ago
- Reactions:1
- Comments:6 (2 by maintainers)
Top GitHub Comments
@DavertMik Still love DI in tests. This reduce boilerplates in code (require several page objects in each tests file). And DI also give you consistency for page objects naming in all tests (you will not call the same page object different in different test files)
Also this option is availible right now. You can load objects from node modules ans paths. (BTW, any global requirement is a PAIN 😁)
agree but looks like not a problem. page objects are not a “frontend” of tests. And you can use any mechanism to load them in other page objects.
Also this looks critical
So in my opinion, if it possible to support require and DI, it’s better to support both
And no breaking changes at all 😉
@DavertMik Is there a separate issue for test migration script? I’ve created an separate repository implementing basic migration using jscodeshift. It’s still in a POC state and has been tested using npm link (windows 10). https://github.com/DawidTabak/codeceptjs-migrate20-include