2016-09-30 11 views
7

Vấn đề là việc nhạo báng trong Typescript có thể trở nên phức tạp nếu đối tượng đủ phức tạp (cũng như trong bất kỳ ngôn ngữ gõ mạnh nào). Bạn thường sẽ thử một số công cụ bổ sung chỉ để biên dịch mã và trong C# chẳng hạn, bạn có thể sử dụng AutoFixture hoặc tương tự. Mặt khác Javascript là ngôn ngữ động và chỉ có thể giả lập một phần của đối tượng cần thiết để chạy thử nghiệm.Mocking trong các bài kiểm tra unitcript Unit

Vì vậy, trong thử nghiệm đơn vị Loại, tôi có thể khai báo sự phụ thuộc của tôi bằng cách sử dụng loại any và do đó dễ dàng giả lập nó. Bạn có thấy bất kỳ hạn chế của cách tiếp cận như vậy?

let userServiceMock: MyApp.Services.UserService = { 
    // lots of thing to mock 
} 

vs

let userServiceMock: any = { 
    user: { 
     setting: { 
      showAvatar: true 
     } 
    } 
} 

Trả lời

10

Kinh nghiệm của tôi với các bài kiểm tra đơn vị trong nguyên cảo chắc chắn cho thấy rằng nó có giá trị để giữ tất cả các đối tượng giả gõ. Khi bạn để lại mocks của bạn với một loại any nó sẽ trở thành vấn đề trong quá trình đổi tên. IDE sẽ không phát hiện chính xác sự xuất hiện của thông số user hoặc settings cần được thay đổi. Tất nhiên viết đối tượng giả bằng tay với một giao diện hoàn chỉnh thực sự mất thời gian.

Cũng may là có hai công cụ cho nguyên cảo cho phép tạo kiểu an các đối tượng giả: ts-mockito (lấy cảm hứng từ Java mockito) và typemoq (lấy cảm hứng từ C# Moq).

+2

tôi đã viết một bài báo so sánh hai thư viện: https://medium.com/@michal.m.stocki/when-it-comes-to-mocking-in-typescript-be8531d39327 – Terite

+0

Tôi đã viết công cụ của riêng tôi xử lý vấn đề tương tự này và tôi muốn một số phản hồi về vấn đề này: https://medium.com/default-to-open/unit-testing-with-angular-and-ineeda-76746a0c8f58 – phenomnomnominal

1

Như được chỉ ra bởi @Terite any loại trên mocks là sự lựa chọn kém vì sẽ không có mối quan hệ giữa mô hình và loại thực tế/triển khai thực tế của nó. Vì vậy, cải tiến giải pháp có thể được đúc tượng một phần-chế giễu để chế giễu loại:

export interface UserService { 
    getUser: (id: number) => User; 
    saveUser: (user: User) => void; 
    // ... number of other methods/fields 
} 

.......

let userServiceMock: UserService = <UserService> { 
    saveUser(user: User) { console.log("save user"); } 
} 
spyOn(userServiceMock, 'getUser').andReturn(new User()); 
expect(userServiceMock.getUser).toHaveBeenCalledWith(expectedUserId); 

Nó cũng đáng nhắc đến rằng nguyên cảo sẽ không cho phép bỏ bất kỳ đối tượng đó có thêm thành viên (siêu hoặc loại có nguồn gốc). Có nghĩa là mô hình một phần của bạn thực sự là loại cơ sở cho UserService và có thể được đúc một cách an toàn. ví dụ.

// Error: Neither type '...' nor 'UserService' is assignable to the other. 
let userServiceMock: UserService = <UserService> { 
    saveUser(user: User) { console.log("save user"); }, 
    extraFunc:() => { } // not available in UserService 
} 
+0

Tại sao, là nó rất quan trọng đối với bạn để có một số đối tượng giả lập? Đó là ý tưởng tồi khi có các trường công cộng và các phương thức công khai trong cùng một dịch vụ. Có thể tốt hơn để định nghĩa phương thức 'setAvatarVisibility (visible: boolean)' và giữ trường 'user' riêng tư. Sau đó, bạn sẽ tránh gián điệp thủ công. Lưu ý rằng trong 'spyOn (userServiceMock, 'saveUser');' tên chuỗi của một phương thức sẽ không được tự động tái cấu trúc bởi IDE. – Terite

+0

Tuy nhiên, một giải pháp tốt hơn là giữ đối tượng 'người dùng' bên ngoài dịch vụ. Mã dễ kiểm tra hơn nếu chúng tôi giữ các dịch vụ không quốc tịch. Bạn nghĩ gì về việc làm cho 'UserService' nhận người dùng đến phương thức của nó:' saveUser (user: User): void; '? – Terite

+0

Tôi đã sử dụng mã này làm ví dụ duy nhất, có lẽ không phải là mã tốt nhất, nhưng nó không phải là thứ mà tôi đang cố gắng chế nhạo. Tôi chỉ đang tìm một giải pháp chung. Và có ví dụ của bạn là tốt hơn tất nhiên, nhờ lời khuyên. –

Các vấn đề liên quan