mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Merge pull request #3058 from tractorcow/pulls/injector-stack-tests
API Injector supports nesting
This commit is contained in:
commit
bbd7bba11f
@ -210,6 +210,13 @@ class Injector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The injector instance this one was copied from when Injector::nest() was called.
|
||||||
|
*
|
||||||
|
* @var Injector
|
||||||
|
*/
|
||||||
|
protected $nestedFrom = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a user wants to use the injector as a static reference
|
* If a user wants to use the injector as a static reference
|
||||||
*
|
*
|
||||||
@ -227,9 +234,38 @@ class Injector {
|
|||||||
* Sets the default global injector instance.
|
* Sets the default global injector instance.
|
||||||
*
|
*
|
||||||
* @param Injector $instance
|
* @param Injector $instance
|
||||||
|
* @return Injector Reference to new active Injector instance
|
||||||
*/
|
*/
|
||||||
public static function set_inst(Injector $instance) {
|
public static function set_inst(Injector $instance) {
|
||||||
self::$instance = $instance;
|
return self::$instance = $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make the newly active {@link Injector} be a copy of the current active
|
||||||
|
* {@link Injector} instance.
|
||||||
|
*
|
||||||
|
* You can then make changes to the injector with methods such as
|
||||||
|
* {@link Injector::inst()->registerService()} which will be discarded
|
||||||
|
* upon a subsequent call to {@link Injector::unnest()}
|
||||||
|
*
|
||||||
|
* @return Injector Reference to new active Injector instance
|
||||||
|
*/
|
||||||
|
public static function nest() {
|
||||||
|
$current = self::$instance;
|
||||||
|
|
||||||
|
$new = clone $current;
|
||||||
|
$new->nestedFrom = $current;
|
||||||
|
return self::set_inst($new);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the active Injector back to the Injector instance the current active
|
||||||
|
* Injector object was copied from.
|
||||||
|
*
|
||||||
|
* @return Injector Reference to restored active Injector instance
|
||||||
|
*/
|
||||||
|
public static function unnest() {
|
||||||
|
return self::set_inst(self::$instance->nestedFrom);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -201,13 +201,15 @@ class Config {
|
|||||||
* A use case for replacing the active configuration set would be for
|
* A use case for replacing the active configuration set would be for
|
||||||
* creating an isolated environment for unit tests.
|
* creating an isolated environment for unit tests.
|
||||||
*
|
*
|
||||||
* @return Config
|
* @param Config $instance New instance of Config to assign
|
||||||
|
* @return Config Reference to new active Config instance
|
||||||
*/
|
*/
|
||||||
public static function set_instance($instance) {
|
public static function set_instance($instance) {
|
||||||
self::$instance = $instance;
|
self::$instance = $instance;
|
||||||
|
|
||||||
global $_SINGLETONS;
|
global $_SINGLETONS;
|
||||||
$_SINGLETONS['Config'] = $instance;
|
$_SINGLETONS['Config'] = $instance;
|
||||||
|
return $instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -215,23 +217,27 @@ class Config {
|
|||||||
* {@link Config} instance.
|
* {@link Config} instance.
|
||||||
*
|
*
|
||||||
* You can then make changes to the configuration by calling update and
|
* You can then make changes to the configuration by calling update and
|
||||||
* remove on the new value returned by Config::inst(), and then discard
|
* remove on the new value returned by {@link Config::inst()}, and then discard
|
||||||
* those changes later by calling unnest.
|
* those changes later by calling unnest.
|
||||||
|
*
|
||||||
|
* @return Config Reference to new active Config instance
|
||||||
*/
|
*/
|
||||||
public static function nest() {
|
public static function nest() {
|
||||||
$current = self::$instance;
|
$current = self::$instance;
|
||||||
|
|
||||||
$new = clone $current;
|
$new = clone $current;
|
||||||
$new->nestedFrom = $current;
|
$new->nestedFrom = $current;
|
||||||
self::set_instance($new);
|
return self::set_instance($new);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the active Config back to the Config instance the current active
|
* Change the active Config back to the Config instance the current active
|
||||||
* Config object was copied from.
|
* Config object was copied from.
|
||||||
|
*
|
||||||
|
* @return Config Reference to new active Config instance
|
||||||
*/
|
*/
|
||||||
public static function unnest() {
|
public static function unnest() {
|
||||||
self::set_instance(self::$instance->nestedFrom);
|
return self::set_instance(self::$instance->nestedFrom);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,3 +7,5 @@
|
|||||||
user login name between sessions, and disable browser auto-completion on the username field.
|
user login name between sessions, and disable browser auto-completion on the username field.
|
||||||
Note that users of certain browsers who have previously autofilled and saved login credentials
|
Note that users of certain browsers who have previously autofilled and saved login credentials
|
||||||
will need to clear their password autofill history before this setting is properly respected.
|
will need to clear their password autofill history before this setting is properly respected.
|
||||||
|
* Test cases that rely on updating and restoring `[api:Injector]` services may now take advantage
|
||||||
|
of the new `Injector::nest()` and `Injector::unnest()` methods to sandbox their alterations.
|
@ -192,6 +192,30 @@ would
|
|||||||
* Create a MySQLDatabase class, passing dbusername and dbpassword as the
|
* Create a MySQLDatabase class, passing dbusername and dbpassword as the
|
||||||
parameters to the constructor
|
parameters to the constructor
|
||||||
|
|
||||||
|
### Testing with Injector in a sandbox environment
|
||||||
|
|
||||||
|
In situations where injector states must be temporarily overridden, it is possible
|
||||||
|
to create nested Injector instances which may be later discarded, reverting the
|
||||||
|
application to the original state.
|
||||||
|
|
||||||
|
This is useful when writing test cases, as certain services may be necessary to
|
||||||
|
override for a single method call.
|
||||||
|
|
||||||
|
For instance, a temporary service can be registered and unregistered as below:
|
||||||
|
|
||||||
|
:::php
|
||||||
|
// Setup default service
|
||||||
|
Injector::inst()->registerService(new LiveService(), 'ServiceName');
|
||||||
|
|
||||||
|
// Test substitute service temporarily
|
||||||
|
Injector::nest();
|
||||||
|
Injector::inst()->registerService(new TestingService(), 'ServiceName');
|
||||||
|
$service = Injector::inst()->get('ServiceName');
|
||||||
|
// ... do something with $service
|
||||||
|
Injector::unnest();
|
||||||
|
|
||||||
|
// ... future requests for 'ServiceName' will return the LiveService instance
|
||||||
|
|
||||||
|
|
||||||
### What are Services?
|
### What are Services?
|
||||||
|
|
||||||
|
@ -13,6 +13,9 @@ class DirectorTest extends SapphireTest {
|
|||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
|
// Required for testRequestFilterInDirectorTest
|
||||||
|
Injector::nest();
|
||||||
|
|
||||||
// Hold the original request URI once so it doesn't get overwritten
|
// Hold the original request URI once so it doesn't get overwritten
|
||||||
if(!self::$originalRequestURI) {
|
if(!self::$originalRequestURI) {
|
||||||
@ -42,7 +45,7 @@ class DirectorTest extends SapphireTest {
|
|||||||
// TODO Remove director rule, currently API doesnt allow this
|
// TODO Remove director rule, currently API doesnt allow this
|
||||||
|
|
||||||
// Remove base URL override (setting to false reverts to default behaviour)
|
// Remove base URL override (setting to false reverts to default behaviour)
|
||||||
Director::setBaseURL(false);
|
Config::inst()->update('Director', 'alternate_base_url', false);
|
||||||
|
|
||||||
// Reinstate the original REQUEST_URI after it was modified by some tests
|
// Reinstate the original REQUEST_URI after it was modified by some tests
|
||||||
$_SERVER['REQUEST_URI'] = self::$originalRequestURI;
|
$_SERVER['REQUEST_URI'] = self::$originalRequestURI;
|
||||||
@ -52,6 +55,8 @@ class DirectorTest extends SapphireTest {
|
|||||||
$_SERVER[$header] = $value;
|
$_SERVER[$header] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Injector::unnest();
|
||||||
|
|
||||||
parent::tearDown();
|
parent::tearDown();
|
||||||
}
|
}
|
||||||
@ -80,7 +85,7 @@ class DirectorTest extends SapphireTest {
|
|||||||
|
|
||||||
$rootURL = Director::protocolAndHost();
|
$rootURL = Director::protocolAndHost();
|
||||||
$_SERVER['REQUEST_URI'] = "$rootURL/mysite/sub-page/";
|
$_SERVER['REQUEST_URI'] = "$rootURL/mysite/sub-page/";
|
||||||
Director::setBaseURL('/mysite/');
|
Config::inst()->update('Director', 'alternate_base_url', '/mysite/');
|
||||||
|
|
||||||
// Test already absolute url
|
// Test already absolute url
|
||||||
$this->assertEquals($rootURL, Director::absoluteURL($rootURL));
|
$this->assertEquals($rootURL, Director::absoluteURL($rootURL));
|
||||||
@ -387,8 +392,6 @@ class DirectorTest extends SapphireTest {
|
|||||||
|
|
||||||
$processor = new RequestProcessor(array($filter));
|
$processor = new RequestProcessor(array($filter));
|
||||||
|
|
||||||
$currentProcessor = Injector::inst()->get('RequestProcessor');
|
|
||||||
|
|
||||||
Injector::inst()->registerService($processor, 'RequestProcessor');
|
Injector::inst()->registerService($processor, 'RequestProcessor');
|
||||||
|
|
||||||
$response = Director::test('some-dummy-url');
|
$response = Director::test('some-dummy-url');
|
||||||
@ -413,9 +416,6 @@ class DirectorTest extends SapphireTest {
|
|||||||
|
|
||||||
// preCall 'false' will trigger an exception and prevent post call execution
|
// preCall 'false' will trigger an exception and prevent post call execution
|
||||||
$this->assertEquals(2, $filter->postCalls);
|
$this->assertEquals(2, $filter->postCalls);
|
||||||
|
|
||||||
// swap back otherwise our wrapping test execution request may fail in the post processing later
|
|
||||||
Injector::inst()->registerService($currentProcessor, 'RequestProcessor');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +75,28 @@ class ConfigStaticTest_Combined3 extends ConfigStaticTest_Combined2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ConfigTest extends SapphireTest {
|
class ConfigTest extends SapphireTest {
|
||||||
|
|
||||||
|
public function testNest() {
|
||||||
|
|
||||||
|
// Check basic config
|
||||||
|
$this->assertEquals(3, Config::inst()->get('ConfigTest_DefinesFooAndBar', 'foo'));
|
||||||
|
$this->assertEquals(3, Config::inst()->get('ConfigTest_DefinesFooAndBar', 'bar'));
|
||||||
|
|
||||||
|
// Test nest copies data
|
||||||
|
Config::nest();
|
||||||
|
$this->assertEquals(3, Config::inst()->get('ConfigTest_DefinesFooAndBar', 'foo'));
|
||||||
|
$this->assertEquals(3, Config::inst()->get('ConfigTest_DefinesFooAndBar', 'bar'));
|
||||||
|
|
||||||
|
// Test nested data can be updated
|
||||||
|
Config::inst()->update('ConfigTest_DefinesFooAndBar', 'foo', 4);
|
||||||
|
$this->assertEquals(4, Config::inst()->get('ConfigTest_DefinesFooAndBar', 'foo'));
|
||||||
|
$this->assertEquals(3, Config::inst()->get('ConfigTest_DefinesFooAndBar', 'bar'));
|
||||||
|
|
||||||
|
// Test unnest restores data
|
||||||
|
Config::unnest();
|
||||||
|
$this->assertEquals(3, Config::inst()->get('ConfigTest_DefinesFooAndBar', 'foo'));
|
||||||
|
$this->assertEquals(3, Config::inst()->get('ConfigTest_DefinesFooAndBar', 'bar'));
|
||||||
|
}
|
||||||
|
|
||||||
public function testUpdateStatic() {
|
public function testUpdateStatic() {
|
||||||
$this->assertEquals(Config::inst()->get('ConfigStaticTest_First', 'first', Config::FIRST_SET),
|
$this->assertEquals(Config::inst()->get('ConfigStaticTest_First', 'first', Config::FIRST_SET),
|
||||||
|
@ -12,6 +12,24 @@ define('TEST_SERVICES', dirname(__FILE__) . '/testservices');
|
|||||||
*/
|
*/
|
||||||
class InjectorTest extends SapphireTest {
|
class InjectorTest extends SapphireTest {
|
||||||
|
|
||||||
|
protected $nestingLevel = 0;
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->nestingLevel = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown() {
|
||||||
|
|
||||||
|
while($this->nestingLevel > 0) {
|
||||||
|
$this->nestingLevel--;
|
||||||
|
Config::unnest();
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
public function testCorrectlyInitialised() {
|
public function testCorrectlyInitialised() {
|
||||||
$injector = Injector::inst();
|
$injector = Injector::inst();
|
||||||
$this->assertTrue($injector->getConfigLocator() instanceof SilverStripeServiceConfigurationLocator,
|
$this->assertTrue($injector->getConfigLocator() instanceof SilverStripeServiceConfigurationLocator,
|
||||||
@ -562,6 +580,64 @@ class InjectorTest extends SapphireTest {
|
|||||||
|
|
||||||
$this->assertInstanceOf('TestObject', $injector->get('service'));
|
$this->assertInstanceOf('TestObject', $injector->get('service'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test nesting of injector
|
||||||
|
*/
|
||||||
|
public function testNest() {
|
||||||
|
|
||||||
|
// Outer nest to avoid interference with other
|
||||||
|
Injector::nest();
|
||||||
|
$this->nestingLevel++;
|
||||||
|
|
||||||
|
// Test services
|
||||||
|
$config = array(
|
||||||
|
'NewRequirementsBackend',
|
||||||
|
);
|
||||||
|
Injector::inst()->load($config);
|
||||||
|
$si = Injector::inst()->get('TestStaticInjections');
|
||||||
|
$this->assertInstanceOf('TestStaticInjections', $si);
|
||||||
|
$this->assertInstanceOf('NewRequirementsBackend', $si->backend);
|
||||||
|
$this->assertInstanceOf('MyParentClass', Injector::inst()->get('MyParentClass'));
|
||||||
|
$this->assertInstanceOf('MyChildClass', Injector::inst()->get('MyChildClass'));
|
||||||
|
|
||||||
|
// Test that nested injector values can be overridden
|
||||||
|
Injector::nest();
|
||||||
|
$this->nestingLevel++;
|
||||||
|
Injector::inst()->unregisterAllObjects();
|
||||||
|
$newsi = Injector::inst()->get('TestStaticInjections');
|
||||||
|
$newsi->backend = new OriginalRequirementsBackend();
|
||||||
|
Injector::inst()->registerService($newsi, 'TestStaticInjections');
|
||||||
|
Injector::inst()->registerService(new MyChildClass(), 'MyParentClass');
|
||||||
|
|
||||||
|
// Check that these overridden values are retrievable
|
||||||
|
$si = Injector::inst()->get('TestStaticInjections');
|
||||||
|
$this->assertInstanceOf('TestStaticInjections', $si);
|
||||||
|
$this->assertInstanceOf('OriginalRequirementsBackend', $si->backend);
|
||||||
|
$this->assertInstanceOf('MyParentClass', Injector::inst()->get('MyParentClass'));
|
||||||
|
$this->assertInstanceOf('MyParentClass', Injector::inst()->get('MyChildClass'));
|
||||||
|
|
||||||
|
// Test that unnesting restores expected behaviour
|
||||||
|
Injector::unnest();
|
||||||
|
$this->nestingLevel--;
|
||||||
|
$si = Injector::inst()->get('TestStaticInjections');
|
||||||
|
$this->assertInstanceOf('TestStaticInjections', $si);
|
||||||
|
$this->assertInstanceOf('NewRequirementsBackend', $si->backend);
|
||||||
|
$this->assertInstanceOf('MyParentClass', Injector::inst()->get('MyParentClass'));
|
||||||
|
$this->assertInstanceOf('MyChildClass', Injector::inst()->get('MyChildClass'));
|
||||||
|
|
||||||
|
// Test reset of cache
|
||||||
|
Injector::inst()->unregisterAllObjects();
|
||||||
|
$si = Injector::inst()->get('TestStaticInjections');
|
||||||
|
$this->assertInstanceOf('TestStaticInjections', $si);
|
||||||
|
$this->assertInstanceOf('NewRequirementsBackend', $si->backend);
|
||||||
|
$this->assertInstanceOf('MyParentClass', Injector::inst()->get('MyParentClass'));
|
||||||
|
$this->assertInstanceOf('MyChildClass', Injector::inst()->get('MyChildClass'));
|
||||||
|
|
||||||
|
// Return to nestingLevel 0
|
||||||
|
Injector::unnest();
|
||||||
|
$this->nestingLevel--;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user