angular / angular/angular-cli

Allow using `vi.mock` (and related) functions in tests

Đang mở
#32,641 4 bình luận 30 reaction 0 người được giao Xem trên GitHub
angular/build:unit-test area: @angular/build
Ngôn ngữ chính
TypeScript
Star
27k
Fork
11.8k
Merge trung bình
14 giờ 23 phút
Pull request đã merge (30 ngày)
162

Mô tả

### Which @angular/* package(s) are relevant/related to the feature request?

core

### Description

With the new Angular unit-test builder (Vite), test files are bundled into unified chunks.
Because of this full bundling step:

- ESM modules are statically linked at build time
- There is no runtime module graph available
- vi.mock() cannot intercept module loading
- Mocking entire modules (components/services/modules) is not possible

Currently if we use `vi.mock` function for automocking component/directives/services/pipes/etc..., we got an error:

```
Error: The "vi.mock" and related methods are not supported with the Angular unit-test system. Please use Angular TestBed for mocking.
```

TestBed overrides are insufficient because:

- They replace Angular metadata (providers/imports), not module implementation
- They cannot replace pure TS logic or side effects
- They do not intercept ESM import bindings

### Proposed solution

Instead of runtime interception (like Vitest normally does), Angular test builder should:

1. Detect `vi.mock()` calls at compile time (in spec files, and in setupFiles)
2. Hoist them
3. Rewrite import bindings to use a generated mock registry
4. Replace module resolution during bundling

I suggest to create esbuild plugin, that:

1. Parse test file AST
2. Detect:
- `vi.mock()`
- `vi.unmock()`
- `vi.doMock()`
- `vi.doUnmock()`
- `vi.importMock()`
- `vi.importActual()`
- `vi.hoisted()`
3. Hoist mock calls
4. Rewrite imports

#### Example Transform Injectable

**Before**:
```typescript
import { MyService } from './my-service';
import { MyOtherService } from './my-other-service';

vi.mock('./my-service', () => ({
MyService:
@Injectable({providedIn: 'root'})
class {
get() { return 'mock'; }
}
}));

vi.mock('./my-other-service');

test(() => {
const myService = new MyService();
const myOtherService = new MyOtherService();
});
```

**After**:
```typescript
// should initialize once in one environment
const __angularViMocks = (globalThis.__angular_vi_mocks ??= new Map());

__angularViMocks.set(
'./my-service',
(() => ({
MyService: class {
get() { return 'mock'; }
static ɵprov = {
providedIn: 'root',
factory: () => new this();
};
static ɵfac = () => new this();
}
}))()
);

__angularViMocks.set(
'./my-other-service',
(() => ({
MyOtherService: class {
get = vi.fn(); // maybe better to put `vi.fn()` to MyOtherService.prototype.get = vi.fn();
static ɵprov = {
providedIn: 'root',
factory: () => new this();
};
static ɵfac = () => new this();
}
}))()
);

import * as __angularViActualMod1 from './my-service';
import * as __angularViActualMod2 from './my-other-service';

// We should replace that import in every dependent chunk.
const { MyService } = __angularViMocks.get('./my-service') ?? __angularViActualMod1;
const { MyOtherService } = __angularViMocks.get('./my-other-service') ?? __angularViActualMod2;
```

### Requirements:

- Must mock full implementation (not only metadata) for any modules, as `vi.mock()` does
- Must stub components, services, modules, pipes, directives both metadata and implementation
- Must preserve ESM live bindings semantics
- Must preserve sourcemaps and coverage
- Must not require runtime module loader

### Alternatives considered

Currently we have no choice, but stay with slow and inefficient `jest` + `jest-preset-angular`.

I've implemented deep-automocking infrastructure for `jest.mock()` [ng-automocks-jest](https://www.npmjs.com/package/ng-automocks-jest), it can stub any angular entities, both metadata and implementation.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Hướng nghiên cứu

Bắt đầu với unit-test builder mới của Angular và đường dẫn bundling bằng Vite của nó, sau đó theo dõi nơi các lệnh gọi vi.mock bị từ chối và cách các tệp spec cùng setupFiles được biến đổi. Xem xét các yêu cầu được đề xuất về phát hiện tại thời điểm biên dịch, hoisting, viết lại import và mock registry. Được xem là hoàn thành khi hỗ trợ các hàm vi được liệt kê, đồng thời vẫn giữ nguyên ESM live bindings, sourcemaps, coverage và không có trình tải mô-đun lúc chạy.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
angular, typescript, vite
Lĩnh vực
build-system, testing-qa
Loại issue
Tính năng
Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức độ hoạt động
Đình trệ
Độ rõ ràng
Cần làm rõ
Mức phù hợp với người mới
20/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.