Overcoming Unit Testing Challenges with OSIsoft PI: Instantiating PIPoint Objects
Explore solutions to unit testing challenges in C# applications using OSIsoft PI SDK. Learn how to instantiate PIPoint objects without a PI Server connection.
Roshan Soni
Overcoming Unit Testing Challenges with OSIsoft PI: Instantiating PIPoint Objects
As developers working with OSIsoft PI System and AFSDK in C#, we often encounter unique challenges when introducing unit testing to applications that interact with PI Archive data. Specifically, when dealing with PIPoint objects, mocking or instantiating these objects in a test environment without an actual PI Server connection can be perplexing. This blog post explores some potential solutions and best practices for handling such scenarios.
Understanding the Challenge
PIPoint is a fundamental class within the PI SDK used to represent points (tags) on the PI Data Archive. Unfortunately, PIPoint is a sealed class with no public constructors, which means it cannot be instantiated directly via the new keyword. Additionally, PIPoint cannot be easily mocked using popular frameworks like Moq, because it is sealed and lacks virtual methods.
The main issue arises when developers want to perform unit testing that involves API calls returning PIPoint objects. In most cases, testing should avoid dependencies on external systems, such as a PI Server, to maintain test isolation and speed.
Exploring Workarounds
While the task seems daunting, there are approaches you can take to overcome these limitations:
-
Use Existing PIPoints:
- If your tests can tolerate some level of integration testing, consider using a well-known
PIPoint, like SINUSOID, available in standard PI Systems. This approach, however, requires server connectivity and deviates from pure unit testing, so it's not ideal for all scenarios.
- If your tests can tolerate some level of integration testing, consider using a well-known
-
Abstract Dependencies:
- Designing your application with abstraction layers can effectively shield your business logic from direct PI SDK dependencies. By wrapping
PIPointand other specific library interactions into interfaces (or service classes), you can leverage dependency injection to provide mock implementations during testing.
- Designing your application with abstraction layers can effectively shield your business logic from direct PI SDK dependencies. By wrapping
-
Designing Test-Friendly Systems:
- If you are developing code from scratch or have control over the architecture, design your system in a way that expects and handles dependency injection gracefully. This practice will facilitate unit testing by allowing partial fakes or complete mock implementations when actual
PIPointinstances aren’t feasible.
- If you are developing code from scratch or have control over the architecture, design your system in a way that expects and handles dependency injection gracefully. This practice will facilitate unit testing by allowing partial fakes or complete mock implementations when actual
-
Using a Proxy or Builder Pattern:
- Consider implementing a factory or builder pattern that handles object creation. This design can wrap the
PIPointobject creation logic and provide default behavior or substitute logic specific to the testing environment.
- Consider implementing a factory or builder pattern that handles object creation. This design can wrap the
-
Investigating Reflection:
- As a last resort, advanced techniques such as using reflection to create instances of non-public constructors may be employed. This approach is fraught with maintenance challenges and potential risks related to updates of the underlying library, so exercise caution and avoid this method if possible.
Conclusion
Introducing unit testing into applications that interact with the PI System can be challenging, but with innovative architectural patterns and strategic planning, these challenges can be mitigated. By thinking critically about dependencies and leveraging design patterns that promote testability, we can foster more robust and reliable software systems.
While the PIPoint class and similar PI SDK components present unique testing challenges, embracing abstraction and designing for testability from the ground up will equip developers with the tools needed to navigate these issues. Remember, the goal of testing frameworks is not only to verify correct behavior but to do so in a way that's quick, repeatable, and independent of environmental constraints such as server availability. Happy testing!
Tags
About Roshan Soni
Expert in PI System implementation, industrial automation, and data management. Passionate about helping organizations maximize the value of their process data through innovative solutions and best practices.
No comments yet
Be the first to share your thoughts on this article.
Related Articles
Enhancing PI ProcessBook Trends with Banding and Zones: User Needs, Workarounds, and the Road Ahead
A look at the user demand for trend banding/zoning in OSIsoft PI ProcessBook, current VBA workarounds, UI challenges, and how future PI Vision releases aim to address these visualization needs.
Roshan Soni
Migrating PIAdvCalcFilVal Uptime Calculations from PI DataLink to PI OLEDB
Learn how to translate PI DataLink's PIAdvCalcFilVal advanced calculations—like counting uptime based on conditions—into efficient PI OLEDB SQL queries. Explore three practical approaches using PIAVG, PIINTERP, and PICOunt tables, and get tips for validation and accuracy.
Roshan Soni
Understanding PI Web API WebID Encoding: Can You Generate WebIDs Client-Side?
Curious about how PI Web API generates WebIDs and whether you can encode them client-side using GUIDs or paths? This article explores the encoding mechanisms, current documentation, and best practices for handling WebIDs in your applications.
Roshan Soni