Angular 2+中如何在不使用TestBed的情况下mock @select属性?
Great question! I’ve dealt with this exact scenario when striving to keep unit tests fast and focused on pure component logic—no TestBed required. Here are a few reliable approaches to mock those Redux @select-decorated Observable properties:
1. Directly Assign the Observable to the Component Instance
Most @select decorators create an instance property that you can overwrite directly after instantiating your component. This is the simplest approach for one-off mocks:
import { of } from 'rxjs'; import { MyComponent } from './my.component'; import { mockUser } from './mocks'; describe('MyComponent', () => { let component: MyComponent; beforeEach(() => { component = new MyComponent(); // Overwrite the @select property with your test Observable component.user$ = of(mockUser); }); it('should process user data correctly', () => { // Your test logic here, using the mocked user$ Observable component.somePureFunction(); expect(...).toBe(...); }); });
If the decorator uses a getter/setter that blocks direct assignment, use Object.defineProperty to force the value:
Object.defineProperty(component, 'user$', { value: of(mockUser), writable: true, configurable: true });
2. Mock the @select Decorator Globally
If you have multiple components using @select and want a reusable mock, override the decorator itself at the module level. For example, if you’re using @ngrx/store:
// At the top of your test file, before importing the component jest.mock('@ngrx/store', () => ({ Select: jest.fn(() => (target: any, propertyKey: string) => { // Initialize the property with a default empty Observable (or mock value) Object.defineProperty(target, propertyKey, { value: of(null), writable: true }); }) })); // Now when you import and instantiate your component, all @select properties will be mockable
This works with most Redux libraries that use class decorators for selectors—adjust the import path to match your library (e.g., @angular-redux/store).
3. Modify the Component Prototype
If the @select property is defined on the component’s prototype (common with some decorator implementations), you can set the mock value before creating the instance:
import { of } from 'rxjs'; import { MyComponent } from './my.component'; import { mockUser } from './mocks'; describe('MyComponent', () => { beforeEach(() => { // Set the mock Observable on the prototype MyComponent.prototype.user$ = of(mockUser); }); it('should use the mocked user data', () => { const component = new MyComponent(); // component.user$ will already be your mock Observable expect(component.user$).toBe(of(mockUser)); }); });
All these approaches keep your tests focused on pure component logic without spinning up Angular’s testing module, which is exactly what you’re aiming for. Just adjust the syntax slightly based on the specific Redux library you’re using (decorator names, import paths, etc.).
内容的提问来源于stack exchange,提问作者Michael L.

