Testing Required Interfaces

For example, consider a package B that would like to implement the interface from the previous section on its type MyImplementor:

julia> struct MyImplementor <: MyInterface end
julia> A.myfunc(::MyImplementor) = "Implemented!"

The authors of package B would (rightly so) like to test that they have conformed to the interface, at least insofar that they are dispatching correctly. With RequiredInterfaces.jl, this too can be easily done (think of this next block as placed in runtests.jl):

using Test, RequiredInterfaces
const RI = RequiredInterfaces

@test RI.check_interface_implemented(MyInterface, MyImplementor)
Test Passed

Compare this to what happens when a type doesn't implement the interface correctly:

julia> struct NonImplementor <: MyInterface end
julia> @test RI.check_interface_implemented(MyInterface, NonImplementor)Error During Test at REPL[2]:1 Expression evaluated to non-Boolean Expression: RI.check_interface_implemented(MyInterface, NonImplementor) Value: Tuple{Any, Tuple}[(Main.A.myfunc, (Main.NonImplementor,))] ERROR: There was an error during testing

check_interface_implemented not only detects that the interface wasn't fully implemented, it can also report which signature was missed, and for which function.

If there are a lot of types implementing a specific interface, it's also possible to test all types who claim to implement the interface, or only a subset of them, instead of doing that on per-type basis:

Julia Bug

The first testset below should in reality produce an error, due to not all subtypes of MyInterface actually implementing the interface. However, due to a bug in Julia (see this issue), MyInterface claims to not have any subtypes, in spite of the fact that the subtypes have MyInterface as their supertype, leading to an empty testset. As a workaround, there is a second testset using the explicit collection version to check the subtypes manually, to show the expected failure. This bug in Julia should not impact the functionality of this package.

julia> struct AnotherImplementor <: MyInterface end
julia> A.myfunc(::AnotherImplementor) = "I'm different!"
julia> @testset "Test all subtypes" RI.check_implementations(MyInterface);Test Summary: |Time Test all subtypes | None 0.4s
julia> @testset "Test all subtypes" RI.check_implementations(MyInterface, [AnotherImplementor, MyImplementor, NonImplementor]);Main.NonImplementor: Error During Test at /home/runner/work/RequiredInterfaces.jl/RequiredInterfaces.jl/src/RequiredInterfaces.jl:307 Expression evaluated to non-Boolean Expression: check_interface_implemented(interface, implementor) Value: Tuple{Any, Tuple}[(Main.A.myfunc, (Main.NonImplementor,))] Test Summary: | Pass Error Total Time Test all subtypes | 2 1 3 0.1s Interface Check: Main.A.MyInterface | 2 1 3 0.0s Main.AnotherImplementor | 1 1 0.0s Main.MyImplementor | 1 1 0.0s Main.NonImplementor | 1 1 0.0s ERROR: Some tests did not pass: 2 passed, 0 failed, 1 errored, 0 broken.
julia> @testset "Test subset" RI.check_implementations(MyInterface, [AnotherImplementor, MyImplementor]);Test Summary: | Pass Total Time Test subset | 2 2 0.0s