Lädt...

🔧 Testable WinForms Applications (MVP pattern)


Nachrichtenbereich: 🔧 Programmierung
🔗 Quelle: dev.to

Today, WinForms apps mainly belong to legacy code because of increasing popularity of WPF. And when one team decides about the development stack for the brand new desktop application, they mainly vote for WPF. On the other hand, there are demands for WinForms applications when it is needed to upgrade existing software which is tightly coupled with WinForms, demands for higher performance, etc.

Different Approaches

There are many opinions on the subject about testable WinForms apps. Some claim that it's not possible to test WinForms apps because there is a lot of dependency between user events and business logic.

Standard Windows Form contains Designer.cs partial class and a partial class which contains event handlers for user actions. So, if we follow this same principle and try to implement app logic in event handlers, we can conclude that it would be very hard to write tests for this kind of application. But, if we look at this problem from some distance, we can conclude that every application with user interface could be represented as an interaction between three main components: Data, User Interface and Business Logic.

This is the basic idea of MV* patterns. If we succeed to segregate these three components, then our application is on a good way to be testable. I made a simple WinForms app using MVP pattern. The entire project could be fetched from github.

In this post, I would just use code snippets to give a general idea of how the code looks like.

But First, Few Words About MVP Pattern

MVP stands for Model-View-Presenter. Model is a component that contains data. It is just a data holder for our forms. View represents the User Interface. It contains design description of our form. Presenter is a component that does most of the job in our WinForms app. It is subscribed to view events and those events are results of user interaction with our form (clicks on buttons, text change, selection change, etc.) and OS interaction with our form (load, show, paint, etc.). Presenter needs to handle all these events and, after their processing, make appropriate action on the view.

Code Structure

Let's start with our view component.

public interface IProductView
{
     event EventHandler ViewLoad;
     event EventHandler<ProductViewModel> AddNewProduct;
     event EventHandler<ProductViewModel> ModifyProduct;
     event EventHandler<int> DeleteProduct;
     event EventHandler<int> ProductSelected;

     void PopulateDataGridView(IList<Product> products);
     void ClearInputControls();
     void ShowMessage(string message);
}

public partial class Products : Form, IProductView
{
    ...
}

We created an interface IProductView that defines rules how Presenter and User Interface component will interact. Now, let's take a look at our presenter component.

public class ProductPresenter
{
    private IProductView view;
    private IProductDataAccess dataAccesService;

    public ProductPresenter(IProductView view, IProductDataAccess dataAccesService)
    {
        this.view = view;
        this.dataAccesService = dataAccesService;
        SubsribeToViewEvents();
    }

    private void SubsribeToViewEvents()
    {
        view.ViewLoad += View_Load;
        view.AddNewProduct += View_AddNewProduct;
        view.ProductSelected += View_ProductSelected;
        view.ModifyProduct += View_ModifyProduct;
        view.DeleteProduct += View_DeleteProduct;
    }
    ...
}

Our presenter takes IProductView interface in its constructor. By this way, our view (form) can easily be replaced with another view which implements the same interface. Also, when it comes to mocking, we can easily inject this dependency through constructor. Another component is IProductDataAccess which represents database interface.

public interface IProductDataAccess
{
    IList<Product> GetAllProducts();
    Product GetProduct(int id);
    bool AddProduct(Product product);
    bool DeleteProduct(int productId);
    bool EditProduct(int productId, Product product);

    string ErrorMessage { get; }
}


One Test Case Example

This test case shows how we can easily mock external dependencies in Presenter component.

 [Test]
 public void ExpectToCallAddProductOnAppropriateEventReceived()
 {
     IProductView view = Substitute.For<IProductView>();
     IProductDataAccess dataAccess = Substitute.For<IProductDataAccess>();
     ProductPresenter presenter = new ProductPresenter(view, dataAccess);

     ProductViewModel viewModel = new ProductViewModel()
     {
         NameText = "Test",
         PriceText = "2"
     };

     view.AddNewProduct += 
          Raise.Event<EventHandler<ProductViewModel>>(view, viewModel);
     dataAccess.Received().AddProduct(Arg.Is<Product>
                           (x=>x.Price == 2 && x.Name == "Test"));
 }


Conclusion

This is just a simple example of how powerful an architecture that starts with an abstraction can be. It is always better to start with some components on the higher level and then, defining the interfaces, you define rules how those components will interact with each other.

...

🔧 Testable WinForms Applications (MVP pattern)


📈 78.86 Punkte
🔧 Programmierung

🔧 How to Become a Kentico MVP - Interview with Kentico MVP Dmitry Bastron


📈 29.85 Punkte
🔧 Programmierung

🔧 Validate Your SaaS Idea with MVP: A Crucial Step in SaaS MVP Development


📈 29.85 Punkte
🔧 Programmierung

🔧 Wizard of Oz MVP, Concierge MVP &amp; More - Which to Choose?


📈 29.85 Punkte
🔧 Programmierung

🔧 Building a fully testable Lovable clone agent in Python with Scenario


📈 24.88 Punkte
🔧 Programmierung

🔧 How Go Interfaces Help Build Clean, Testable Systems


📈 24.88 Punkte
🔧 Programmierung

🔧 Building Testable Websites: Empower Your Entire Organization


📈 24.88 Punkte
🔧 Programmierung

🔧 React+Vitest to write testable components, common problems record.


📈 24.88 Punkte
🔧 Programmierung

🔧 Reusable, Extensible and Testable State Logic with Reactive Programming.


📈 24.88 Punkte
🔧 Programmierung

🔧 Building Testable CloudFront Functions with TypeScript


📈 24.88 Punkte
🔧 Programmierung

🔧 Creating a Testable Facade in Laravel


📈 24.88 Punkte
🔧 Programmierung

🔧 Strategies for Writing More Testable Code - An Imperative Approach


📈 24.88 Punkte
🔧 Programmierung

🔧 Checking Efx - testable effects for Elixir


📈 24.88 Punkte
🔧 Programmierung

🔧 Testable Apps: Why You Should Consider The Composable Architecture


📈 24.88 Punkte
🔧 Programmierung

🔧 How To Make Legacy Code More Testable


📈 24.88 Punkte
🔧 Programmierung

🔧 Testable Go Code


📈 24.88 Punkte
🔧 Programmierung

🔧 Runme: Road to Testable Docs


📈 24.88 Punkte
🔧 Programmierung

🎥 Building a scalable, modularized, testable app from scratch


📈 24.88 Punkte
🎥 Video | Youtube

🎥 Build Testable Apps for Android (Google I/O'19)


📈 24.88 Punkte
🎥 Video | Youtube

🔧 How to create an Installer for a Winforms application using ClickOnce for Visual Studio 2022


📈 22.51 Punkte
🔧 Programmierung

🎥 What's New with WinForms in .NET 9? | OD537


📈 22.51 Punkte
🎥 Video | Youtube

🎥 May the forms be with you: a new hope with Blazor Hybrid on WinForms | ODFP623


📈 22.51 Punkte
🎥 Video | Youtube

🔧 How to create an Installer for a Winforms application using Wix for Visual Studio 2022


📈 22.51 Punkte
🔧 Programmierung

📰 WinForms Designer: Alternative für für 32-Bit .NET Framework Projekte


📈 22.51 Punkte
📰 IT Nachrichten

🔧 "Choosing the Right .NET Desktop Application: WinForms vs. WPF vs. Console"


📈 22.51 Punkte
🔧 Programmierung

📰 Microsofts Strategie zu WinForms in einer 64-Bit-Welt


📈 22.51 Punkte
🤖 Android Tipps

🕵️ CVE-2024-10013 | Progress Telerik UI for WinForms 2024.2.514/2024.3.924 deserialization


📈 22.51 Punkte
🕵️ Sicherheitslücken

🔧 GrapeCity’s ComponentOne 2023 v1 includes a new multi-column combobox for the WinForms edition


📈 22.51 Punkte
🔧 Programmierung

🐧 Modern.Forms: Cross-platform spiritual successor to Winforms for .NET 6


📈 22.51 Punkte
🐧 Linux Tipps

🔧 Accessibility and Perf improvements to WinForms | On .NET


📈 22.51 Punkte
🔧 Programmierung

🎥 Supercharge Your WinForms Projects with .NET 9 Roslyn Analyzers


📈 22.51 Punkte
🎥 Video | Youtube

🔧 App Center for WPF and WinForms


📈 22.51 Punkte
🔧 Programmierung