Sử dụng Moq và xem xét Callback
nhưng tôi không thể tìm thấy một ví dụ đơn giản để hiểu cách sử dụng nó.
Bạn có một đoạn mã làm việc nhỏ giải thích rõ ràng cách thức và thời điểm sử dụng nó không?
Sử dụng Moq và xem xét Callback
nhưng tôi không thể tìm thấy một ví dụ đơn giản để hiểu cách sử dụng nó.
Bạn có một đoạn mã làm việc nhỏ giải thích rõ ràng cách thức và thời điểm sử dụng nó không?
Câu trả lời:
Khó đánh bại https://github.com/Moq/moq4/wiki/Quickstart
Nếu điều đó không đủ rõ ràng, tôi sẽ gọi đó là lỗi tài liệu ...
CHỈNH SỬA: Để đáp lại sự làm rõ của bạn ...
Đối với mỗi phương pháp chế nhạo Setup
bạn thực hiện, bạn phải chỉ ra những điều như:
Cơ .Callback
chế nói rằng "Tôi không thể mô tả nó ngay bây giờ, nhưng khi một cuộc gọi có hình dạng như thế này xảy ra, hãy gọi lại cho tôi và tôi sẽ làm những gì cần phải làm". Là một phần của cùng một chuỗi cuộc gọi thông thạo, bạn có thể kiểm soát kết quả để trả về (nếu có) thông qua .Returns
". Trong các ví dụ QS, một ví dụ là chúng làm cho giá trị được trả về tăng lên mỗi lần.
Nói chung, bạn sẽ không cần một cơ chế như thế này thường xuyên (Mẫu thử nghiệm xUnit có các thuật ngữ cho phản vật chất của Thử nghiệm logic có điều kiện ilk), và nếu có bất kỳ cách nào đơn giản hơn hoặc có sẵn để thiết lập những gì bạn cần, nó sẽ được sử dụng trong sở thích.
Phần 3 của 4 trong loạt phim Moq của Justin Etheringge đề cập đến nó, và có một ví dụ khác về gọi lại ở đây
Một ví dụ đơn giản về gọi lại có thể được tìm thấy tại Sử dụng Gọi lại với bài đăng Moq .
Callback
không liên quan gì đến giá trị trả về (trừ khi bạn tình cờ liên kết nó thông qua mã). Về cơ bản, nó chỉ đảm bảo rằng lệnh gọi lại được gọi trước hoặc sau mỗi lần gọi (tùy thuộc vào việc bạn xâu chuỗi nó trước hay sau Returns
tương ứng), thuần túy và đơn giản.
Dưới đây là một ví dụ về việc sử dụng một lệnh gọi lại để kiểm tra một thực thể được gửi đến Dịch vụ dữ liệu xử lý phần chèn.
var mock = new Mock<IDataService>();
DataEntity insertedEntity = null;
mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1)
.Callback((DataEntity de) => insertedEntity = de);
Cú pháp phương pháp chung thay thế:
mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1)
.Callback<DataEntity>(de => insertedEntity = de);
Sau đó, bạn có thể kiểm tra một cái gì đó như
Assert.AreEqual("test", insertedEntity.Description, "Wrong Description");
It.Is<T>
trong một Mock.Verify
thay vì xả rác kiểm tra với nhiệt độ. Nhưng +1 vì tôi cá rằng có rất nhiều người sẽ hoạt động tốt nhất từ một ví dụ.
Có hai loại Callback
trong Moq. Một xảy ra trước khi cuộc gọi trở lại; cái kia xảy ra sau khi cuộc gọi trở lại.
var message = "";
mock.Setup(foo => foo.Execute(arg1: "ping", arg2: "pong"))
.Callback((x, y) =>
{
message = "Rally on!";
Console.WriteLine($"args before returns {x} {y}");
})
.Returns(message) // Rally on!
.Callback((x, y) =>
{
message = "Rally over!";
Console.WriteLine("arg after returns {x} {y}");
});
Trong cả hai lệnh gọi lại, chúng ta có thể:
Callback
chỉ đơn giản là một phương tiện để thực thi bất kỳ mã tùy chỉnh nào bạn muốn khi một lệnh gọi được thực hiện đến một trong các phương thức của mô hình. Đây là một ví dụ đơn giản:
public interface IFoo
{
int Bar(bool b);
}
var mock = new Mock<IFoo>();
mock.Setup(mc => mc.Bar(It.IsAny<bool>()))
.Callback<bool>(b => Console.WriteLine("Bar called with: " + b))
.Returns(42);
var ret = mock.Object.Bar(true);
Console.WriteLine("Result: " + ret);
// output:
// Bar called with: True
// Result: 42
Gần đây tôi đã gặp phải một trường hợp sử dụng thú vị cho nó. Giả sử bạn mong đợi một số cuộc gọi đến mô phỏng của mình, nhưng chúng xảy ra đồng thời. Vì vậy, bạn không có cách nào để biết thứ tự mà họ sẽ được gọi, nhưng bạn muốn biết các cuộc gọi mà bạn mong đợi đã diễn ra (bất kể thứ tự). Bạn có thể làm điều gì đó như sau:
var cq = new ConcurrentQueue<bool>();
mock.Setup(f => f.Bar(It.IsAny<bool>())).Callback<bool>(cq.Enqueue);
Parallel.Invoke(() => mock.Object.Bar(true), () => mock.Object.Bar(false));
Console.WriteLine("Invocations: " + String.Join(", ", cq));
// output:
// Invocations: True, False
BTW đừng nhầm lẫn bởi sự phân biệt "trước Returns
" và "sau Returns
" gây hiểu lầm . Nó chỉ đơn thuần là sự phân biệt kỹ thuật về việc mã tùy chỉnh của bạn sẽ chạy sau khi Returns
đã được đánh giá hay trước đó. Trong mắt của người gọi, cả hai sẽ chạy trước khi giá trị được trả về. Thật vậy, nếu phương thức đang void
quay lại, bạn thậm chí không thể gọi được Returns
và nó vẫn hoạt động như cũ. Để biết thêm thông tin, hãy xem https://stackoverflow.com/a/28727099/67824 .
Ngoài các câu trả lời hay khác ở đây, tôi đã sử dụng nó để thực hiện logic trước khi đưa ra một ngoại lệ. Ví dụ: tôi cần lưu trữ tất cả các đối tượng đã được chuyển cho một phương thức để xác minh sau này và phương thức đó (trong một số trường hợp thử nghiệm) cần phải đưa ra một ngoại lệ. Gọi .Throws(...)
về Mock.Setup(...)
ghi đè các Callback()
hành động và không bao giờ gọi nó. Tuy nhiên, bằng cách đưa ra một ngoại lệ trong Callback, bạn vẫn có thể thực hiện tất cả những thứ tốt mà một callback cung cấp và vẫn có một ngoại lệ.