Unit testing components

I have read through many posts about testing for Grasshopper plugins (unit testing, integration testing etc). I have not found an answer to this simple question:

Is it possible to unit test custom components in a custom plugin? I would imagine a process like open Grasshopper document in headless mode, insert component, set inputs, solve, check output/validation messages.

It is not easy to unit-test due to one big problem.

You cannot call Rhinocommon in a unit-testing framework. You need Rhino to be started. This is a limitation because of the licensing. But if you start Rhino or Grasshopper headless, then its debatable if you really perform a unit-test or already an integration test.

In general one solution is to mock out un-testable code. You could write your plugin to have two dlls. One is the .gha(renamed .dll) which is basically doing nothing more than piping input and output to actual logic which is encapsulated into the second .dll. You could develop standalone inside the second dll and do whatever you want. You cannot use Rhinocommon then anymore in this second dll. This is for most plugins a problem, for all of those which rely on Rhino functionality. You could use the Rhino3dm Nuget package to replace Rhinocommon, but in my understanding this is a limited replacement of Rhinocommon. Likely they have extended this. The advantage of this approach is that you can write an additionial CLI or GUI application and only use the .gha for interfacing. But this is really up to the use-case

Another option is to forget about unit-tests, and instead use assertions and guards inside the code. With Trace.Assert/Debug.Assert you put assumption inside every method you write. With Guards you strictly limit input.
Then integration testing your plugin with simple test definitions might be sufficient. I prefer this when I write software for prototyping.

1 Like

Thanks for the details, most of which I did not know. I have found Rhino.Testing to be quite helpful with loading Rhino and Grasshopper in headless mode. I could easily tailor this to suit my needs.

As far as “unit testing” is concerned, I may end up creating .gh files with each component in all possible input permutations, so as to test the component solution validations. This is easy since the .gh file is loaded using GH_DocumentIO.Open which returns an instance of GH_Document where all components can be queried.

What I do is I unit-test components in 1 big script with Brontosaurus | Food4Rhino.

Check this file as a reference: Megarachne/Megarachne/TestsInsideGrasshopper/GrasshopperTests.gh at main · paireks/Megarachne · GitHub

Isn’t this more of a integration/blackbox test? I mean Curve-To-Graph is probably a component which has very few lines of code. But what if you provide a component which calls 3 classes with 5 methods each? There is no way you can prove that the internal logic works. The advantage of unit-testing frameworks is that you can write small test cases and access any part of your source code as long as its callable/testable. Lets assume you are not calling any Rhinocommon for building the curvature graph. Given that this component is doing this. I don’t know.
How can you prove that the graph itself builds correctly in a more broader sense? Sure you can provide one reference curve as you did, but it would be quite tedious to check it this way. Instead you want to do write a test-cases like e.g. where you check if the normal lines have the correct spacing etc…

I personally avoid it, I mean I tend to not have any logic in the components themselves, they only call 1 specific method most of the time (with some exceptions).

I’m okay with calling it blackbox tests if you prefer.

Just wanted to share one of the methods how I test components. I prepare different cases of inputs and check if the outputs are as I expected it to be for each component. I design components as input → output behaviour only, I tend to avoid mutations inside components or any other side effects or components without outputs.

I’m not trying to say that you do something wrong. And as I said its always debatable. The differences are about the test objectives and the scope.

Usually you want to fail as early as possible and a unit test helps you to pinpoint a problem while development. Even before compiling the entire solution. If you can create unit-tests in Visual Studio, without running Rhino you have tons of advantages.
A software unit is not a Grasshopper component. It can be, but this is a special case.
As an external developer you see that a Grasshopper component has been tested, but you have no information about whether this is supposed to be a single software unit, or if the component is complex enough to hold an entire application.

Therefore you test a black box (=unknown software) only with ability to use the given interface. Such tests are useful to see that it works in a rather positive scenario but its really hard to write good and defensive tests. Therefore I find any existing solutions in the RH/GH domain quite unsatisfying.