---
title: Unit testing
description: Many SDK classes interact with PingFederate through the com.pingidentity.sdk.internal.services.ServiceFactory object, which looks up an implementation of some internal service at runtime using the JDK's ServiceLoader.
component: pingfederate
version: 13.1
page_id: pingfederate:sdk_developers_guide:pf_unit_testing
canonical_url: https://docs.pingidentity.com/pingfederate/13.1/sdk_developers_guide/pf_unit_testing.html
llms_txt: https://docs.pingidentity.com/pingfederate/llms.txt
docs_for_agents: https://developer.pingidentity.com/build-with-ai/docs-for-agents.md
section_ids:
  steps: Steps
  example: Example
---

# Unit testing

Many SDK classes interact with PingFederate through the `com.pingidentity.sdk.internal.services.ServiceFactory` object, which looks up an implementation of some internal service at runtime using the JDK's `ServiceLoader`.

Your plugin won't usually call `ServiceFactory` directly. Instead, it will call the `TrustedCAAccessor` helper in the `com.pingidentity.access` package, which looks like an ordinary method call but actually depends on the server to supply a real implementation underneath.

For testing purposes there's no real implementation for `ServiceFactory` to find outside of a running server. It's also not practical to mock the functions of `ServiceFactory`.

The way around this is to write your own implementation of the service interface and register it as an alternate `ServiceLoader` provider. `ServiceFactory` will find it automatically when your test is running, just as it would the real provider in production.

## Steps

1. Write a test implementation of the service interface.

   ```java
   package com.pingidentity.clientregistration;

   import com.pingidentity.sdk.internal.interfaces.CertWrapper;
   import com.pingidentity.sdk.internal.services.interfaces.TrustedCAAccessorService;

   public class TestTrustedCAAccessorService implements TrustedCAAccessorService
   {
       @Override
       public Set<TrustAnchor> getAllTrustAnchors()
       {
           Set<TrustAnchor> trustAnchors = new HashSet<>();
           trustAnchors.add(new TrustAnchor(getTestCertificate(), null));
           return trustAnchors;
       }

       @Override
       public Set<CertWrapper> getTrustedCAs()
       {
           return Collections.emptySet();
       }
   }
   ```

2. To register the test implementation for `ServiceLoader`, add a file to the plugin folder under `src/test/resources/META-INF/services/` named after the fully-qualified interface it implements.

   ```none
   src/test/resources/META-INF/services/com.pingidentity.sdk.internal.services.interfaces.TrustedCAAccessorService
   ```

   The contents of the file are just the fully-qualified name of your test class.

   ```none
   com.pingidentity.clientregistration.TestTrustedCAAccessorService
   ```

3. Call the accessor as normal in your test.

   `ServiceLoader` only detects what's on the classpath that it's run with, so during a test run it finds your test implementation instead of a production implementation:

   ```java
   @Test
   public void getTrustedCertificatesReturnsTestServiceCertificate()
   {
       SoftwareStatementValidatorPlugin plugin = new SoftwareStatementValidatorPlugin();

       Collection<X509Certificate> trustedCertificates = plugin.getTrustedCertificates();

       assertEquals(1, trustedCertificates.size());
   }
   ```

   Since `src/test/resources` never gets packaged into the plugin jar, this substitution only works for testing purposes. Real deployments still resolve the genuine PingFederate-provided service.

## Example

You can test this pattern using the `pf-server/sdk/examples/dynamic-client-registration/` example. Its plugin, `SoftwareStatementValidatorPlugin`, fetches a JWKS URI over HTTPS to verify a signature, and needs a trust store to do that. This trust store is built from `TrustedAccessor.getAllTrustAnchors()`, the server's imported certificate authority and self-signed certificates. That lookup is located in a small package-private method called `getTrustedCertificates()`, specifically so a test can call it directly.

The following table describes the test setup:

| Container                                                                                                         | Description                                                                                      |
| ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `src/test/java/com/pingidentity/clientregistration/TestTrustedCAAccessorService.java`                             | The test implementation of `TrustedCAAccessorService`, backed by a fixed self-signed certificate |
| `src/test/resources/META-INF/services/com.pingidentity.sdk.internal.services.interfaces.TrustedCAAccessorService` | The `ServiceLoader` registration file                                                            |
| `src/test/java/com/pingidentity/clientregistration/SoftwareStatementValidatorPluginTest.java`                     | The JUnit test. Calling `getTrustedCertificates()` and checking it returns the test certificate  |

From the example plugin's directory, run `mvn test`.
