2013-06-11 42 views
23

Tôi đang sử dụng Moq và muốn tạo các lớp trình tạo để tạo các mocks của tôi với các giá trị mặc định hợp lý có thể được ghi đè trong khi thiết lập thử nghiệm khi cần thiết. Cách tiếp cận tôi đã sử dụng các phương pháp mở rộng trong đó tôi chuyển các giá trị tham số đầu vào và đầu ra dự kiến. Trong khi làm như vậy, tôi thấy hành vi khác nhau trong những gì dường như với tôi là mã tương đương ngữ nghĩa: truyền It.IsAny() trực tiếp trong một thiết lập vs truyền giá trị của It.IsAny() gián tiếp trong một thiết lập. Ví dụ:Sự khác biệt giữa việc chuyển It.IsAny <int>() và giá trị của It.IsAny <int>() thành thiết lập phương thức

public interface IFoo 
{ 
    bool Bar(int value); 
    bool Bar2(int value); 
} 
public class Foo : IFoo 
{ 
    public bool Bar(int value) { return false; } 
    public bool Bar2(int value) { return false; } 
} 

var mock = new Mock<IFoo>(); 
mock.Setup(x => x.Bar(It.IsAny<int>())).Returns(true); 
Assert.IsTrue(mock.Object.Bar(123));     // Succeeds 

var myValue = It.IsAny<int>(); 
mock.Setup(x => x.Bar2(myValue)).Returns(true); 
Assert.IsTrue(mock.Object.Bar2(123));     // Fails 

Cả hai cuộc gọi tương đương (với tôi), nhưng cuộc gọi đến xác nhận không thành công của Bar2. Tại sao điều này?

Trả lời

33

It.IsAny chỉ cho phép Moq khớp các yêu cầu gọi phương thức trong tương lai nếu được sử dụng trong cấu trúc Setup. Khi Setup được gọi là Moq, chỉ cần thêm lệnh gọi phương thức vào bộ nhớ cache của các cuộc gọi phương thức đã thiết lập. Lưu ý rằng đối số cho Setup trong ví dụ của bạn có loại Expression<Func<IFoo, bool>>. Vì bạn đang đi qua một số Expression, cuộc gọi phương thức thực tế không được gọi và Moq có khả năng duyệt qua biểu thức để tìm ra tham số nào của cuộc gọi phương thức là rõ ràng và đối số là It.IsAny. Nó sử dụng khả năng này để xác định xem một cuộc gọi phương thức trong tương lai tại thời gian chạy có khớp với một trong các cuộc gọi phương thức đã được thiết lập hay không.

Để làm cho nó để các phương pháp Bar thể chấp nhận lập luận It.IsAny<int>(), nó là cần thiết để làm cho It.IsAny<int>() trở lại một int (vì đó là kiểu của các tham số của Bar). Nói chung, loại trả lại của It.IsAny<T> phải là T. Giá trị tùy ý là T phải được chọn. Lựa chọn tự nhiên nhất là default(T), hoạt động cho các loại tham chiếu và loại giá trị. (Đọc thêm về từ khóa mặc định here). Trong trường hợp của bạn, đó là default(int), là 0.

Vì vậy, khi bạn thực sự đánh giá It.IsAny<int>() giá trị của 0 sẽ được trả lại ngay lập tức. Tuy nhiên, khi bạn sử dụng It.IsAny<int>() trong một Expression (như trong đối số với phương thức Setup), thì cấu trúc cây của cuộc gọi phương thức được giữ nguyên và Moq có thể khớp lệnh gọi phương thức tương lai với cuộc gọi phương thức được đóng gói theo số Expression.

Vì vậy, mặc dù bạn không thể giữ It.IsAny<int>() như là một biến trong bất kỳ cách có ý nghĩa, bạn có thể giữ toàn bộ Expression trong một biến:

Expression<Func<IFoo, bool>> myExpr = x => x.Bar2(It.IsAny<int>()); 
mock.Setup(myExpr).Returns(true); 
Assert.IsTrue(mock.Object.Bar2(123)); 

Cuối cùng, tôi chỉ muốn nhắc nhở bạn rằng Moq là mã nguồn mở. Nguồn có sẵn here. Tôi thấy có giá trị khi có mã nguồn đó để tôi có thể nhấp vào và khám phá mã và kiểm tra đơn vị.

+1

Giải thích tuyệt vời, Ben. Tôi đã nghi ngờ vấn đề nằm trong đánh giá biểu hiện, như bạn mô tả, thay vì giá trị trả lại của It.IsAny (). Tôi nhìn vào nguồn phản chiếu trong ILSpy và có thể thấy It.IsAny , nhưng không thể lấy được cái đầu của tôi quấn quanh cách nó hoạt động liên quan đến vấn đề. Cảm ơn một lần nữa vì đã giải thích. –

+0

Cảm ơn bạn đã thanh toán bù trừ cho phù thủy đó cho tôi! Tôi đã cố gắng nói "var anyFooParam = It.IsAny (); var anyBarParam = It.IsAny ();" để xem liệu nó có làm cho các bài kiểm tra của tôi dễ đọc hơn như: .Setup (mock => mock.method (anyFooParam, anyBarParam)). –

1

It.IsAny<int>() có kiểu trả về của int và trả về 0, vì vậy thiết lập thứ hai của bạn là tương đương với:

mock.Setup(x => x.Bar2(0)).Returns(true); 

tôi không kiểm tra mã moq, nhưng tôi khá chắc chắn rằng khi đánh giá biểu thức trong phương thức thiết lập, nó tính đến tham số thực sự là It.IsAny so với một số bình thường.

Bạn nên tắt nếu bạn tạo các thiết lập trực tiếp trong các phương thức trợ giúp của bạn và không vượt qua It.IsAny.