diff --git a/packages/core/test/acceptance/env_injector_standalone_spec.ts b/packages/core/test/acceptance/env_injector_standalone_spec.ts
new file mode 100644
index 00000000000..f98d857da30
--- /dev/null
+++ b/packages/core/test/acceptance/env_injector_standalone_spec.ts
@@ -0,0 +1,73 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {Component, createEnvironmentInjector, importProvidersFrom, NgModule} from '@angular/core';
+
+describe('environement injector and standalone components', () => {
+ it('should see providers from modules imported by standalone components', () => {
+ class ModuleService {}
+
+ @NgModule({providers: [ModuleService]})
+ class Module {
+ }
+
+ @Component({standalone: true, imports: [Module]})
+ class StandaloneComponent {
+ }
+
+ const envInjector = createEnvironmentInjector(importProvidersFrom(StandaloneComponent));
+ expect(envInjector.get(ModuleService)).toBeInstanceOf(ModuleService);
+ });
+
+ it('should see providers when exporting a standalone components', () => {
+ class ModuleService {}
+
+ @NgModule({providers: [ModuleService]})
+ class Module {
+ }
+
+ @Component({standalone: true, imports: [Module]})
+ class StandaloneComponent {
+ }
+
+ @NgModule({imports: [StandaloneComponent], exports: [StandaloneComponent]})
+ class AppModule {
+ }
+
+ const envInjector = createEnvironmentInjector(importProvidersFrom(AppModule));
+ expect(envInjector.get(ModuleService)).toBeInstanceOf(ModuleService);
+ });
+
+ it('should not collect duplicate providers', () => {
+ class ModuleService {}
+
+ @NgModule({providers: [{provide: ModuleService, useClass: ModuleService, multi: true}]})
+ class Module {
+ }
+
+ @Component({standalone: true, imports: [Module]})
+ class StandaloneComponent1 {
+ }
+
+ @Component({standalone: true, imports: [Module]})
+ class StandaloneComponent2 {
+ }
+
+ @NgModule({
+ imports: [StandaloneComponent1, StandaloneComponent2],
+ exports: [StandaloneComponent1, StandaloneComponent2]
+ })
+ class AppModule {
+ }
+
+ const envInjector = createEnvironmentInjector(importProvidersFrom(AppModule));
+ const services = envInjector.get(ModuleService) as ModuleService[];
+
+ expect(services.length).toBe(1);
+ });
+});
diff --git a/packages/core/test/acceptance/standalone_injector_spec.ts b/packages/core/test/acceptance/standalone_injector_spec.ts
new file mode 100644
index 00000000000..6c755e5066c
--- /dev/null
+++ b/packages/core/test/acceptance/standalone_injector_spec.ts
@@ -0,0 +1,96 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {Component, ComponentFactoryResolver, createEnvironmentInjector, EnvironmentInjector, Injector, NgModule, ViewChild, ViewContainerRef} from '@angular/core';
+import {TestBed} from '@angular/core/testing';
+
+describe('standalone injector', () => {
+ it('should create one standalone injector for each parent EnvInjector', () => {
+ let counter = 0;
+
+ class Service {
+ value = counter++;
+ }
+
+ @NgModule({providers: [Service]})
+ class ModuleWithAService {
+ }
+
+ @Component({
+ selector: 'standalone',
+ standalone: true,
+ imports: [ModuleWithAService],
+ template: `({{service.value}})`
+ })
+ class TestComponent {
+ constructor(readonly service: Service) {}
+ }
+
+ @Component({selector: 'app', template: ``})
+ class AppComponent {
+ @ViewChild('insert', {static: true, read: ViewContainerRef}) vcRef!: ViewContainerRef;
+
+ createComponent(envInjector?: EnvironmentInjector): void {
+ this.vcRef.createComponent(TestComponent, {environmentInjector: envInjector});
+ }
+ }
+
+ const fixture = TestBed.createComponent(AppComponent);
+ const currEnvInjector = TestBed.inject(EnvironmentInjector);
+
+ fixture.componentInstance.createComponent(currEnvInjector);
+ fixture.detectChanges();
+ expect(fixture.nativeElement.textContent).toBe('(0)');
+
+ // inserting the same standalone component second time and asserting that no new injector /
+ // service instance gets created
+ fixture.componentInstance.createComponent(currEnvInjector);
+ fixture.detectChanges();
+ expect(fixture.nativeElement.textContent).toBe('(0)(0)');
+
+ // inserting with a different EnvInjector as a parent should trigger a new service instance
+ // creation
+ fixture.componentInstance.createComponent(createEnvironmentInjector([], currEnvInjector));
+ fixture.detectChanges();
+ expect(fixture.nativeElement.textContent).toBe('(0)(0)(1)');
+ });
+
+ it('should create a standalone Injector for ComponentRefs that are not inserted anywhere', () => {
+ class Service {
+ value = 'Service value';
+ }
+
+ @NgModule({providers: [Service]})
+ class ModuleWithAService {
+ }
+
+ @Component({
+ selector: 'standalone',
+ standalone: true,
+ imports: [ModuleWithAService],
+ template: `{{service.value}}`
+ })
+ class DynamicComponent {
+ constructor(readonly service: Service) {}
+ }
+
+ @Component({})
+ class AppComponent {
+ }
+
+ const fixture = TestBed.createComponent(AppComponent);
+
+ const cfr = TestBed.inject(ComponentFactoryResolver);
+ const cf = cfr.resolveComponentFactory(DynamicComponent);
+
+ const componentRef = cf.create(Injector.NULL);
+ componentRef.changeDetectorRef.detectChanges();
+
+ expect(componentRef.location.nativeElement.textContent).toBe('Service value');
+ });
+});
diff --git a/packages/core/test/render3/standalone_spec.ts b/packages/core/test/render3/standalone_spec.ts
deleted file mode 100644
index e74cbb23adf..00000000000
--- a/packages/core/test/render3/standalone_spec.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * @license
- * Copyright Google LLC All Rights Reserved.
- *
- * Use of this source code is governed by an MIT-style license that can be
- * found in the LICENSE file at https://angular.io/license
- */
-
-import {InjectionToken, Injector, ɵRenderFlags, ɵɵdefineComponent, ɵɵdefineInjector, ɵɵelement, ɵɵStandaloneFeature} from '../../src/core';
-import {getComponentDef} from '../../src/render3/definition';
-
-describe('standalone components, directives and pipes', () => {
- it('should filter out imported NgModules from a list of directives to match', () => {
- const token = new InjectionToken('TestToken');
-
- let count = 0;
- class TestModule {
- static ɵinj = ɵɵdefineInjector({
- providers: [{
- provide: token,
- useFactory:
- () => {
- return `From module: ${count++}`;
- }
- }]
- });
- }
-
- class TestComponent {
- static ɵfac = () => new TestComponent();
- static ɵcmp = ɵɵdefineComponent({
- type: TestComponent,
- selectors: [['test-cmp']],
- decls: 1,
- vars: 0,
- template:
- (rf: ɵRenderFlags, ctx: TestComponent) => {
- if (rf & ɵRenderFlags.Create) {
- ɵɵelement(0, 'div');
- }
- },
- dependencies: [TestModule],
- features: [ɵɵStandaloneFeature],
- standalone: true
- });
- }
-
- // simulate logic executed by ComponentFactory::create
- const def = getComponentDef(TestComponent);
-
- if (def !== null && def.getStandaloneInjector !== null) {
- const parentInjector = Injector.create({providers: []});
- expect(def.getStandaloneInjector(parentInjector)!.get(token)).toEqual('From module: 0');
-
- // subsequent calls with the same same parent injector should not create new provider
- // instances
- expect(def.getStandaloneInjector(parentInjector)!.get(token)).toEqual('From module: 0');
-
- // but specifying a different injector _should_ create a new provider instance
- const otherParentInjector = Injector.create({providers: []});
- expect(def.getStandaloneInjector(otherParentInjector)!.get(token)).toEqual('From module: 1');
-
- // again, same calls should not create new instances
- expect(def.getStandaloneInjector(parentInjector)!.get(token)).toEqual('From module: 0');
- expect(def.getStandaloneInjector(otherParentInjector)!.get(token)).toEqual('From module: 1');
- } else {
- fail('Failed to get component def or standalone feature function');
- }
- });
-});