Để thêm vào câu trả lời tuyệt vời của emddudley, lợi ích lớn nhất bạn có thể nhận được khi chế nhạo dịch vụ là có thể kiểm tra những gì sẽ xảy ra khi dịch vụ không hoạt động chính xác. Mã giả thử nghiệm có thể trông giống như thế này:
public int AgeMinimumValue_LogsServiceError_Test()
{
ClassUnderTest uut = new ClassUnderTest();
MockService service = new MockService();
service.Throws(new TimeoutException());
MockLogger logger = new MockLogger();
try {
int age = uut.getAge(service, logger);
Assert.Fail("Exception was not raised by the class under test");
}
catch (TimeoutException) {
Assert(logger.LogError().WasCalled());
}
}
Và bây giờ việc thực hiện của bạn đã được chỉnh sửa với yêu cầu mới này
public int getAge(Service s, Logger l)
{
try {
int age = s.execute(empId);
return age;
}
catch(Exception ex) {
l.LogError(ex);
throw;
}
}
Trong các kịch bản khác, nhiều khả năng bạn sẽ cần phải trả lời các phản hồi phức tạp hơn. Nếu dịch vụ cung cấp xử lý thẻ tín dụng, bạn sẽ cần phản hồi Thành công, Dịch vụ không khả dụng, Thẻ tín dụng đã hết hạn, Số không hợp lệ, v.v. Bằng cách chế tạo dịch vụ, bạn có thể đảm bảo bạn đáp ứng các tình huống này theo cách phù hợp với tình huống của bạn. Trong trường hợp này, bạn phải giả định đầu vào / đầu ra từ dịch vụ và phản hồi bạn nhận được từ việc biết rằng mã tiêu thụ sẽ hoạt động cho tất cả các đầu ra đã biết thực sự có ý nghĩa và có giá trị.
EDIT: Tôi chỉ nhận thấy bạn muốn có thể giả định mà không sửa đổi phương thức hiện có. Để làm điều này, locate("ageservice");
phương thức sẽ cần phải được thay đổi để hỗ trợ các đối tượng giả trong các thử nghiệm và định vị dịch vụ thực sự một khi nó đã sẵn sàng. Đây là một biến thể của công cụ định vị dịch vụ , tóm tắt logic để truy xuất việc triển khai dịch vụ bạn đang sử dụng. Một phiên bản nhanh có thể trông như thế này:
public Service locate(string serviceToLocate) {
if(testEnvironment) // Test environment should be set externally
return MockService(serviceToLocate);
else
return Service(serviceToLocate);
}
Tuy nhiên, khuyến nghị của tôi là chuyển các phụ thuộc dịch vụ vào Trình xây dựng:
public int AgeMinimumValue_LogsServiceError_Test()
{
MockService service = new MockService();
service.Throws(new TimeoutException());
MockLogger logger = new MockLogger();
ClassUnderTest uut = new ClassUnderTest(service, logger);
try {
int age = uut.getAge();
Assert.Fail("Exception was not raised by the class under test");
}
catch (TimeoutException) {
Assert(logger.LogError().WasCalled());
}
}
Bây giờ phương thức getAge không còn có trách nhiệm tra cứu dịch vụ vì nó đã bị trừu tượng hóa khỏi lớp hoàn toàn để lại một triển khai tương tự như sau:
public int getAge()
{
try {
// _service is a private field set by the constructor
int age = _service.execute(empId);
return age;
}
catch(Exception ex) {
// _logger is a private field set by the constructor
_logger.LogError(ex);
throw;
}
}