Running tests on code defined in package extension

Hello,

Is there any way to run unit tests on methods/types defined in a package extension (but not exposed by the main package) ?

As an example, say that our package exposes only one function:
sample(model, rng ....)
We write an extension to define sample on a different model type, but for that to work we need to wrap the model and run a few pre-processing steps before running sample(wrapped(model), rng...).
I’d like to write some tests on the intermediate functions defined in the extension without exposing them in the main package.

Just load the other dependent package during your tests, which will load the package extension.

1 Like

That wouldn’t expose the functions defined in the extension code that are not exposed in the main package

If you extend a function by definitions (i.e. import something and add new signatures to it) then loading both solves the problem

If you define new functions in the extension, these might not even be in global scope – ever, if I understand extensions correctly for now.
So how would you normally call these functions then? The same way you would do the test.

edit: Sorry I was not careful enough in reading – you import the sample function in your extension so you add method definitions to it, these are actually available when loading both packages as @mkitti already wrote.

Example: Manifolds.jl has an NLSolve extension, that also does the same as yours, it extends a method by further signatures… and here
Manifolds.jl/approx_inverse_retraction.jl at master · JuliaManifolds/Manifolds.jl · GitHub is the test – Manifolds.jl is loaded either before or through utils.jl, so with NLSolve together the extension and its functionality is loaded and afterwards tested.

Thanks for the help, that explains half of my problem. Should have given a bit more details, in my case the sample function is quite complicated and I split it in a few smaller functions, something like this

module MainPackage
  function sample end
  export sample
end

module MainPackageExt

 function subfn() end
 function wrapper() end

 function sample()
   subfn(...)
   wrapper(...)
 end
end

Just wondering if there’s a way to unit test subfn instead of just testing MainPackageExt.sample

if you use import MainPackage: sample
as the first line within MainPackageExt you can call both sample functions with just sample() (if you happen to have a default implementation without the extension).

For subfn directly I am not aware how that could directly be tested, but can you issue sample-calls that end up covering all cases of subfn?

PS: I just saw atop the topic here – Welcome to the Julia Discourse! :partying_face:

PPS: In naming, the extension Names I saw until now are MainPackageSecondPackageExt when you main package gets extended once the second package is loaded.

I encountered a similar issue. My workaround was to define a stub in MainPackage:

module MainPackage
  function sample end
  function subfun end 
  export sample
end

In the extension module, I imported subfn:

module MainPackageExt
 import MainPackage: subfn
 function subfn() end
 function wrapper() end

 function sample()
   subfn(...)
   wrapper(...)
 end
end

In my test block, I did the following:

@testset "subfn" begin 
  using MainPackage 
  using WeakDependency
  using MainPackage: subfn

  ...

end

That seemed to accomplish both goals: keeping subfn hidden from the user, but allowing it to be explicitly loaded for the purpose of testing. I don’t know if there is a better approach but this seems to check the boxes.

I’d avoid introducing names in the package if you are only doing it to test extensions, a user absolutely can access it. I would also avoid testing subfunctions so finely, I prefer tests for public names or longlasting code. But since there are always good exceptions, Base.get_extension gets you the module instance of a loaded extension. You still can’t import any names from it (that’s what packages are for, not extensions), but it’ll let you examine it like any module, and you can assign it or its properties to a variable for convenience. Obviously don’t abuse this to make extensions act like hampered packages if new names warrant a true package.

Thank you. Base.get_extension is what I need.