Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Tại sao việc sử dụng Unit Test là một khoản đầu tư lớn vào Kiến trúc chất lượng cao

Tôi đã quyết định viết bài này để chứng minh rằng các bài kiểm tra đơn vị không chỉ là một công cụ để vật lộn với hồi quy trong mã mà còn là một khoản đầu tư tuyệt vời vào một kiến ​​trúc chất lượng cao. Ngoài ra, một chủ đề trong cộng đồng .NET tiếng Anh đã thúc đẩy tôi thực hiện điều này. Tác giả của bài báo là Johnnie. Anh ấy mô tả ngày đầu tiên và cuối cùng của mình ở công ty liên quan đến việc phát triển phần mềm cho hoạt động kinh doanh trong lĩnh vực tài chính. Johnnie đang ứng tuyển vào vị trí - một nhà phát triển các bài kiểm tra đơn vị. Anh ấy khó chịu với chất lượng mã kém mà anh ấy phải kiểm tra. Anh ta so sánh mật mã với một bãi phế liệu được nhồi với những vật thể sao chép lẫn nhau ở bất kỳ vị trí nào không phù hợp. Ngoài ra, anh ta không thể tìm thấy các kiểu dữ liệu trừu tượng trong một kho lưu trữ:mã chỉ chứa liên kết các triển khai yêu cầu chéo lẫn nhau.

Johnnie nhận ra tất cả sự vô dụng của việc kiểm tra mô-đun trong công ty này đã vạch ra tình huống này cho người quản lý, từ chối hợp tác thêm và đưa ra một lời khuyên có giá trị. Ông khuyến nghị rằng một nhóm phát triển nên tham gia các khóa học để tìm hiểu các đối tượng khởi tạo và sử dụng các kiểu dữ liệu trừu tượng. Tôi không biết liệu người quản lý có làm theo lời khuyên của anh ấy hay không (tôi nghĩ anh ấy đã không làm). Tuy nhiên, nếu bạn quan tâm đến ý của Johnnie và cách sử dụng kiểm thử mô-đun có thể ảnh hưởng đến chất lượng kiến ​​trúc của bạn, bạn có thể đọc bài viết này.

Cách ly phụ thuộc là cơ sở của thử nghiệm mô-đun

Kiểm tra mô-đun hoặc đơn vị là kiểm tra xác minh chức năng của mô-đun được cách ly khỏi các phần phụ thuộc của nó. Sự cô lập phụ thuộc là sự thay thế các đối tượng trong thế giới thực, trong đó mô-đun đang được thử nghiệm tương tác, với các sơ khai mô phỏng hành vi chính xác của các nguyên mẫu của chúng. Sự thay thế này cho phép tập trung vào việc kiểm tra một mô-đun cụ thể, bỏ qua một hành vi không chính xác có thể có của môi trường của nó. Một sự cần thiết để thay thế các phụ thuộc trong thử nghiệm gây ra một thuộc tính thú vị. Một nhà phát triển nhận ra rằng mã của họ sẽ được sử dụng trong các bài kiểm tra mô-đun phải phát triển bằng cách sử dụng trừu tượng và thực hiện tái cấu trúc ở những dấu hiệu đầu tiên của khả năng kết nối cao.

Tôi sẽ xem xét nó trên một ví dụ cụ thể.

Hãy thử tưởng tượng mô-đun tin nhắn cá nhân có thể trông như thế nào trên hệ thống được phát triển bởi công ty mà Johnnie đã thoát ra từ đó. Và cùng một mô-đun sẽ trông như thế nào nếu các nhà phát triển áp dụng thử nghiệm đơn vị.

Mô-đun sẽ có thể lưu trữ thông báo trong cơ sở dữ liệu và nếu người mà thông báo được gửi đến là trong hệ thống - hãy hiển thị thông báo trên màn hình cùng với thông báo nâng ly.

//A module for sending messages in C#. Version 1.
public class MessagingService
{
    public void SendMessage(Guid messageAuthorId, Guid messageRecieverId, string message)
    {
        //A repository object stores a message in a database
        new MessagesRepository().SaveMessage(messageAuthorId, messageRecieverId, message);
        //check if the user is online  
        if (UsersService.IsUserOnline(messageRecieverId))
        {
            //send a toast notification calling the method of a static object  
            NotificationsService.SendNotificationToUser(messageAuthorId, messageRecieverId, message);
        }
    }
}

Hãy kiểm tra xem mô-đun của chúng ta có những phụ thuộc nào.

Hàm SendMessage gọi các phương thức tĩnh của các đối tượng Notificationsservice và Usersservice và tạo đối tượng Messagesrepository chịu trách nhiệm làm việc với cơ sở dữ liệu.

Không có vấn đề gì với thực tế là mô-đun tương tác với các đối tượng khác. Vấn đề là tương tác này được tạo ra như thế nào và nó không được tạo thành công. Quyền truy cập trực tiếp vào các phương pháp của bên thứ ba đã làm cho mô-đun của chúng tôi được liên kết chặt chẽ với các triển khai cụ thể.

Tương tác này có rất nhiều mặt trái, nhưng điều quan trọng là mô-đun Messagingservice đã mất khả năng được kiểm tra tách biệt khỏi việc triển khai Notificationsservice, Usersservice và Messagesrepository. Trên thực tế, chúng tôi không thể thay thế các đối tượng này bằng các đối tượng gốc.

Bây giờ, hãy xem cùng một mô-đun sẽ trông như thế nào nếu một nhà phát triển chăm sóc nó.

//A module for sending messages in C#. Version  2.
public class MessagingService: IMessagingService
{
    private readonly IUserService _userService;
    private readonly INotificationService _notificationService;
    private readonly IMessagesRepository _messagesRepository;

    public MessagingService(IUserService userService, INotificationService notificationService, IMessagesRepository messagesRepository)
    {
        _userService = userService;
        _notificationService = notificationService;
        _messagesRepository = messagesRepository;
    }

    public void AddMessage(Guid messageAuthorId, Guid messageRecieverId, string message)
    {
        //A repository object stores a message in a database.  
        _messagesRepository.SaveMessage(messageAuthorId, messageRecieverId, message);
        //check if the user is online  
        if (_userService.IsUserOnline(messageRecieverId))
        {
            //send a toast message
            _notificationService.SendNotificationToUser(messageAuthorId, messageRecieverId, message);
        }
    }
}

Như bạn có thể thấy, phiên bản này tốt hơn nhiều. Sự tương tác giữa các đối tượng giờ đây không được xây dựng trực tiếp mà thông qua các giao diện.

Chúng ta không cần truy cập các lớp tĩnh và khởi tạo các đối tượng trong các phương thức với logic nghiệp vụ nữa. Điểm chính là chúng ta có thể thay thế tất cả các phụ thuộc bằng cách chuyển các sơ khai để kiểm tra thành một phương thức khởi tạo. Do đó, trong khi nâng cao khả năng kiểm tra mã, chúng tôi cũng có thể cải thiện cả khả năng kiểm tra của mã và kiến ​​trúc ứng dụng của chúng tôi. Chúng tôi đã từ chối việc triển khai trực tiếp bằng cách sử dụng và chuyển phần khởi tạo cho lớp trên. Đây chính xác là những gì Johnnie muốn.

Tiếp theo, tạo thử nghiệm cho mô-đun gửi tin nhắn.

Đặc điểm kỹ thuật của các bài kiểm tra

Xác định những gì thử nghiệm của chúng tôi sẽ kiểm tra:

  • Một lệnh gọi duy nhất của phương thức SaveMessage
  • Một lệnh gọi duy nhất của phương thức SendNotificationToUser () nếu phương thức IsUserOnline () sơ khai trên đối tượng IUsersService trả về true
  • Không có phương thức SendNotificationToUser () nếu phương thức IsUserOnline () sơ khai trên đối tượng IUsersService trả về false

Việc tuân theo các điều kiện này có thể đảm bảo rằng việc triển khai thông điệp SendMessage là chính xác và không có bất kỳ lỗi nào.

Kiểm tra

Thử nghiệm được thực hiện bằng cách sử dụng khung Moq riêng biệt

[TestMethod]
public void AddMessage_MessageAdded_SavedOnce()
{
    //Arrange
    //sender
    Guid messageAuthorId = Guid.NewGuid();
    //receiver who is online
    Guid recieverId = Guid.NewGuid();
    //a message sent from a sender to a receiver
    string msg = "message";
    // stub for the IsUserOnline interface of the IUserService method
    Mock<IUserService> userServiceStub = new Mock<IUserService>(new MockBehavior());
    userServiceStub.Setup(x => x.IsUserOnline(It.IsAny<Guid>())).Returns(true);
    //mocks for INotificationService and IMessagesRepository
    Mock<INotificationService> notificationsServiceMoq = new Mock<INotificationService>();
    Mock<IMessagesRepository> repositoryMoq = new Mock<IMessagesRepository>();
    //create a module for messages passing mocks and stubs as dependencies 
    var messagingService = new MessagingService(userServiceStub.Object, notificationsServiceMoq.Object,
                                                repositoryMoq.Object);

    //Act
    messagingService.AddMessage(messageAuthorId, recieverId, msg);

    //Assert
    repositoryMoq.Verify(x => x.SaveMessage(messageAuthorId, recieverId, msg), Times.Once);
   
}

[TestMethod]
public void AddMessage_MessageSendedToOffnlineUser_NotificationDoesntRecieved()
{
    //Arrange
    //sender
    Guid messageAuthorId = Guid.NewGuid();
    //receiver who is offline
    Guid offlineReciever = Guid.NewGuid();
    //message sent from a sender to a receiver
    string msg = "message";
    // stub for the IsUserOnline interface of the IUserService method
    Mock<IUserService> userServiceStub = new Mock<IUserService>(new MockBehavior());
    userServiceStub.Setup(x => x.IsUserOnline(offlineReciever)).Returns(false);
    //mocks for INotificationService and IMessagesRepository
    Mock<INotificationService> notificationsServiceMoq = new Mock<INotificationService>();
    Mock<IMessagesRepository> repositoryMoq = new Mock<IMessagesRepository>();
    // create a module for messages passing mocks and stubs as dependencies
    var messagingService = new MessagingService(userServiceStub.Object, notificationsServiceMoq.Object,
                                                repositoryMoq.Object);
    //Act
    messagingService.AddMessage(messageAuthorId, offlineReciever, msg);

    //Assert
    notificationsServiceMoq.Verify(x => x.SendNotificationToUser(messageAuthorId, offlineReciever, msg),
                                    Times.Never);
}

[TestMethod]
public void AddMessage_MessageSendedToOnlineUser_NotificationRecieved()
{
    //Arrange
    //sender
    Guid messageAuthorId = Guid.NewGuid();
    //receiver who is online
    Guid onlineRecieverId = Guid.NewGuid();
    //message sent from a sender to a receiver 
    string msg = "message";
    // stub for the IsUserOnline interface of the IUserService method
    Mock<IUserService> userServiceStub = new Mock<IUserService>(new MockBehavior());
    userServiceStub.Setup(x => x.IsUserOnline(onlineRecieverId)).Returns(true);
    //mocks for INotificationService and IMessagesRepository
    Mock<INotificationService> notificationsServiceMoq = new Mock<INotificationService>();
    Mock<IMessagesRepository> repositoryMoq = new Mock<IMessagesRepository>();
    //create a module for messages passing mocks and stubs as dependencies
    var messagingService = new MessagingService(userServiceStub.Object, notificationsServiceMoq.Object,
                                                repositoryMoq.Object);

    //Act
    messagingService.AddMessage(messageAuthorId, onlineRecieverId, msg);

    //Assert
    notificationsServiceMoq.Verify(x => x.SendNotificationToUser(messageAuthorId, onlineRecieverId, msg),
                                    Times.Once);
}

Tóm lại, tìm kiếm một kiến ​​trúc lý tưởng là một nhiệm vụ vô ích.

Các bài kiểm tra đơn vị rất tốt để sử dụng khi bạn cần kiểm tra kiến ​​trúc khi mất khớp nối giữa các mô-đun. Tuy nhiên, hãy nhớ rằng thiết kế các hệ thống kỹ thuật phức tạp luôn là một sự thỏa hiệp. Không có kiến ​​trúc lý tưởng và không thể tính trước tất cả các kịch bản của quá trình phát triển ứng dụng. Chất lượng kiến ​​trúc phụ thuộc vào nhiều tham số, thường loại trừ lẫn nhau. Bạn có thể giải quyết bất kỳ vấn đề thiết kế nào bằng cách thêm một mức độ trừu tượng bổ sung. Tuy nhiên, nó không đề cập đến vấn đề của một lượng lớn các mức độ trừu tượng. Tôi không khuyên bạn nên nghĩ rằng sự tương tác giữa các đối tượng chỉ dựa trên những điều trừu tượng. Vấn đề là bạn sử dụng mã cho phép tương tác giữa các triển khai và kém linh hoạt hơn, có nghĩa là nó không có khả năng được kiểm tra bằng các bài kiểm tra đơn vị.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Khớp Cung với Cầu - Giải pháp, Phần 3

  2. Thuê hoặc Nhận thuê:Mô hình dữ liệu cho quy trình tuyển dụng

  3. Cách di chuyển cơ sở dữ liệu vào máy chủ người bán lại của bạn

  4. Sử dụng thuật sĩ tổ chức lại ngoại tuyến

  5. SQL CREATE TABLE cho người mới bắt đầu