Skip to content

Instantly share code, notes, and snippets.

@Shawn-Armstrong
Last active May 31, 2023 01:41
Show Gist options
  • Save Shawn-Armstrong/a64812510d1558f59904bf09b7e60574 to your computer and use it in GitHub Desktop.
Save Shawn-Armstrong/a64812510d1558f59904bf09b7e60574 to your computer and use it in GitHub Desktop.

#OpenTAP_pytest.md

Overview

  • This file explores using Python's unit testing framework, PyTest, in conjunction with OpenTAP's Python resources.
  • OpenTAP was built using the .NET framework; Python capabilities were later added via the Python.NET library.
  • Python.NET is a bridge between Python and .NET platforms, enabling interoperation between the two. This tool allows Python programs to access the .NET Framework and Mono libraries; similarly, it provides .NET applications the ability to utilize Python libraries.

Details

  • This demonstration is using Windows 11
  • This demonstration is using Python 3.11.3

Simple Python.NET Example

  • The following example imports the Common Language Runtime (CLR) module from .NET via Python.NET. Afterward, it constructs a string using the .NET framework then uses that object's built-in length method.

string_dotnet.py

'''
NAME: 
    string_dotnet.py

OVERVIEW: 
    This script explores the use of the Python.NET library in Python.

DETAILS:
    Python.NET is a bridge between Python and .NET platforms, enabling interoperation
    between the two. This tool allows Python programs to access the .NET Framework 
    and Mono libraries; similarly, it provides .NET applications the ability to 
    utilize Python libraries.

    This script imports the Common Language Runtime (CLR) module from .NET.

    The function `string_length(s)` illustrates this interoperation. It constructs a
    string `s` via CLR from .NET, and then returns the length of this string.
'''

import clr
clr.AddReference('System')

from System import String

def string_length(s):
    dotnet_string = String(s)
    return dotnet_string.Length

main.py

'''
NAME: 
    main.py

OVERVIEW: 
    This program is the driver to test `string_dotnet.py`.

DETAILS:
    Expected output is 12. See `string_dotnet.py` for details.

'''

import clr                          
import string_dotnet as dotnet

clr.AddReference("System")
from System import Console

print(dotnet.string_length("Hello World!"));

Output

python main.py
12

Simple PyTest with Python.NET Resources

  • Continue using main.py and dotnet_string.py from the Simple Python.NET Example section.
  • The following content uses Python's built-in unit testing framework, PyTest, to test our aformentioned files.
  • Section successfully demonstrates that we can use PyTest in conjunction with Python.NET.

test_my_dotnet_module.py

# test_string_dotnet.py
import string_dotnet as dotnet

def test_dotnet_string_length():
    assert dotnet.string_length("Hello") == 5
    assert dotnet.string_length("") == 0

Output

python -m pytest test_string_dotnet.py
========================================================= test session starts =========================================================
platform win32 -- Python 3.11.3, pytest-7.3.1, pluggy-1.0.0
rootdir: C:\Users\shawn\OneDrive\Desktop\Demo2\OpenTAP PyTest
collected 1 item

test_string_dotnet.py .                                                                                                          [100%]

========================================================== 1 passed in 0.35s ==========================================================

Simple PyTest with OpenTAP Resources

  • Attempting to run PyTest in conjunction with the boiler plate OpenTAP Python project setup.

Filetree

.
├── ...
├── demo/
│   ├── test.py
│   ├── TestInstrument.py
│   └── TestStepExample.py
└── ...

TestStepExample.py

# TestStepExample.py
from opentap import *
from System import Int32
import OpenTap
from .TestInstrument import TestInstrument

@attribute(OpenTap.Display("Test Step Example", "A simple python test step", "demo"))
class TestStepExample (TestStep):
    my_test_instrument = property(TestInstrument).\
        add_attribute(OpenTap.Display(
            "Instrument", "The instrument to use in the step.", "Resources"))

    def __init__(self):
        super(TestStepExample, self).__init__()
    
    def Run(self):
        self.log.Debug("Number of characters: {0}", Int32(self.my_test_instrument.string_length("Hello World!")))

TestInstrument.py

# TestInstrumnet.py
from opentap import *
from System import String
import OpenTap
import clr
clr.AddReference("demo.Api")
from demo.Api import PythonInstrumentApi

@attribute(OpenTap.Display("Test Instrument", "This class implements a shared Python API.", "demo"))
class TestInstrument(Instrument, PythonInstrumentApi):
    def __init__(self):
        "Set up the properties, methods and default values of the instrument."
        super(TestInstrument, self).__init__()
        self.Name = "Test Instrument" 
    
    def string_length(self, s):
        dotnet_string = String(s)
        return dotnet_string.Length

test.py

# test.py
import TestInstrument as dotnet

def test_dotnet_string_length():
    assert dotnet.string_length("Hello") == 5
    assert dotnet.string_length("") == 0

Observations

  • PyTest cannot find opentap module.
  • Module opentap lives in the default OpenTAP isntallation directory at C:\Program Files\OpenTAP\Packages\Python
  • Module opentap is a .NET bridge iteroperating the OpenTAP .NET library so that it can be used by Python.
  • I attempted to add the resource into my Python interpert's path but it failed. The module has a dynamically linked library dependencies; I think this is C:\Program Files\OpenTAP\Packages\Python\OpenTap.Python.dll. Adding this file is likely not a practical solution... It could be though if organized properly but may be too large of an undertaking.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment