[NoBrainer] Simplifying and Speeding Up Testing in Sparx Enterprise Architect

Today’s post will be rather quick. I will show you how a unit/integration test class that can interact with real model data in Sparx Enterprise Architect.

Beyond the well known Interop.EA assembly there is another assembly called SparxSystems.Repository that provides the intersting feature to connect to a running EA instance: Services.GetRepository(). We can use this to write unit/integration tests that are run out of Visual Studio and the preferred test runner of our choice. Here is a test base class which will connect to a running EA instance and (if none is found) create a new EA instance (which of course will take some time):

public abstract class RepositoryTestClassBase : IDisposable
{
  private const string EA_PROCESS_NAME = "EA";
  private const string REPO_FILENAME = "Test.eap";
  private const string REPO_RELATIVE_PATHNAME = ".";

  private static readonly object _syncRoot = new object();

  protected static string RepoPathAndFileName { get; set; } =
    Path.Combine(new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase).AbsolutePath, REPO_RELATIVE_PATHNAME, REPO_FILENAME);

  private static readonly List _cleanupActions = new List();
  private static int _instances;

  private static readonly Lazy _repository = new Lazy(() =>
  {
    foreach (var process in Process.GetProcessesByName(EA_PROCESS_NAME))
    {
      var repository = Services.GetRepository(process.Id);
      if(null == repository?.ConnectionString) continue;
      if(!repository.ConnectionString.EndsWith(REPO_FILENAME)) continue;

      return repository;
    }

    var result = new global::EA.Repository();
    var isOpen = result.OpenFile(RepoPathAndFileName);

    _cleanupActions.Add(() => result.Exit());

    result.ShowWindow(1);
    return result;
  });
  protected static global::EA.Repository Repository => _repository.Value;

  private void ReleaseUnmanagedResources()
  {
    if (0 != Interlocked.Decrement(ref _instances)) return;

    _cleanupActions.Reverse();
    foreach (var action in _cleanupActions)
    {
      action.Invoke();
    }
  }

  protected virtual void Dispose(bool disposing)
  {
    ReleaseUnmanagedResources();
  }

  public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }

  protected RepositoryTestClassBase()
  {
    Interlocked.Increment(ref _instances);

    if (_repository.IsValueCreated) return;

    lock (_syncRoot)
    {
      if (_repository.IsValueCreated) return;

      var result = _repository.Value;
    }
  }

  ~RepositoryTestClassBase()
  {
    Dispose(false);
  }
}

The valuefactory of _repository will check if there is already a running instance of EA to connect to that matches the model file defined at the top of the class. If none is found a new instance will be created which will later automatically be closed after all tests are run (tracked by a synchronised invocation counter). If the instance was already running, it will be left as is.

With this class we can create unit/integration tests as easy as this:

[TestClass]
public class IntegrationTest : RepositoryTestClassBase
{
  [TestMethod]
  public void Test()
  {
    // Arrange

    // Act
    var result = Repository.ConnectionString;

    // Assert
    Assert.IsFalse(string.IsNullOrWhiteSpace(result));
  }
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: