Repository: open-feature/java-sdk
Pull request #1331 (2025-02-15 09:47:08)

test: Reduce usage of singelton within our tests and implementations
Our tests are great, but often we rely on our own Singleton for testing purposes. This can create concurrency issues or make testing really hard. By instantiating a own API object for each test we ensure that we are not messing with each other.
Furthermore we should not use `.getInstance()` within our own implementation, which is now ensured with an ArchUnit test


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [2 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1 New issue](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


sonarqubecloud[bot]: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate failed**  
Failed conditions  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [C Reliability Rating on New Code](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) (required ≥ A)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)

##   
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/light_bulb-16px.png '') Catch issues before they fail your Quality Gate with our IDE extension ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/sonarlint-16px.png '') [SonarQube for IDE](https://www.sonarsource.com/products/sonarlint/features/connected-mode/?referrer=pull-request)


sonarqubecloud[bot]: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate failed**  
Failed conditions  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [C Reliability Rating on New Code](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) (required ≥ A)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)

##   
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/light_bulb-16px.png '') Catch issues before they fail your Quality Gate with our IDE extension ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/sonarlint-16px.png '') [SonarQube for IDE](https://www.sonarsource.com/products/sonarlint/features/connected-mode/?referrer=pull-request)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [9 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [3 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [3 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [3 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [3 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [3 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/open-feature/java-sdk/pull/1331?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-feature) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 93.23%. Comparing base [(`4ba5695`)](https://app.codecov.io/gh/open-feature/java-sdk/commit/4ba5695eeea6a7ab2fe1d2c595fa482d4b7868dc?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-feature) to head [(`22824c8`)](https://app.codecov.io/gh/open-feature/java-sdk/commit/22824c8494b62ea5804a439025c99b28d8414a03?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-feature).

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff              @@
##               main    #1331      +/-   ##
============================================
+ Coverage     93.04%   93.23%   +0.19%     
- Complexity      466      467       +1     
============================================
  Files            43       43              
  Lines          1121     1123       +2     
  Branches         90       90              
============================================
+ Hits           1043     1047       +4     
+ Misses           49       48       -1     
+ Partials         29       28       -1     
```

| [Flag](https://app.codecov.io/gh/open-feature/java-sdk/pull/1331/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-feature) | Coverage Δ | |
|---|---|---|
| [unittests](https://app.codecov.io/gh/open-feature/java-sdk/pull/1331/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-feature) | `93.23% <100.00%> (+0.19%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-feature#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/open-feature/java-sdk/pull/1331?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-feature).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-feature).

aepfli: > I quite like this! The one concern I have would be people not using the singleton causing issues.

The good part is, the constructor is not public but protected, and always have been. Hence either people need to extend the API object, or have to be in the same package. This is already possible, so we are actually not changing anything regarding this behavior 
sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [2 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [3 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [3 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [2 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [2 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [2 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [2 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [3 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [3 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)


sonarqubecloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [3 New issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=open-feature_java-sdk&pullRequest=1331&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=open-feature_java-sdk&pullRequest=1331&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=open-feature_java-sdk&pullRequest=1331)



Patch:
diff --git a/pom.xml b/pom.xml
index a6a54e5..bbc1b8d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,6 +67,13 @@
         </dependency>
 
         <!-- test -->
+        <dependency>
+            <groupId>com.tngtech.archunit</groupId>
+            <artifactId>archunit-junit5</artifactId>
+            <version>1.4.0</version>
+            <scope>test</scope>
+        </dependency>
+
         <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-core</artifactId>
@@ -242,12 +249,14 @@
                     <ignoredUnusedDeclaredDependencies>
                         <ignoredUnusedDeclaredDependency>com.github.spotbugs:*</ignoredUnusedDeclaredDependency>
                         <ignoredUnusedDeclaredDependency>org.junit*</ignoredUnusedDeclaredDependency>
+                        <ignoredUnusedDeclaredDependency>com.tngtech.archunit*</ignoredUnusedDeclaredDependency>
                         <ignoredUnusedDeclaredDependency>org.simplify4u:slf4j2-mock*</ignoredUnusedDeclaredDependency>
                     </ignoredUnusedDeclaredDependencies>
                     <ignoredDependencies>
                         <ignoredDependency>com.google.guava*</ignoredDependency>
                         <ignoredDependency>io.cucumber*</ignoredDependency>
                         <ignoredDependency>org.junit*</ignoredDependency>
+                        <ignoredDependency>com.tngtech.archunit*</ignoredDependency>
                         <ignoredDependency>com.google.code.findbugs*</ignoredDependency>
                         <ignoredDependency>com.github.spotbugs*</ignoredDependency>
                         <ignoredDependency>org.simplify4u:slf4j-mock-common:*</ignoredDependency>
diff --git a/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java b/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java
index 9175a7c..bd60cc7 100644
--- a/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java
+++ b/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java
@@ -29,7 +29,7 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
 
     protected OpenFeatureAPI() {
         apiHooks = new ArrayList<>();
-        providerRepository = new ProviderRepository();
+        providerRepository = new ProviderRepository(this);
         eventSupport = new EventSupport();
         transactionContextPropagator = new NoOpTransactionContextPropagator();
     }
@@ -333,7 +333,7 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
             providerRepository.shutdown();
             eventSupport.shutdown();
 
-            providerRepository = new ProviderRepository();
+            providerRepository = new ProviderRepository(this);
             eventSupport = new EventSupport();
         }
     }
diff --git a/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java b/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java
index 66f25f6..a393d83 100644
--- a/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java
+++ b/src/main/java/dev/openfeature/sdk/OpenFeatureClient.java
@@ -507,7 +507,7 @@ public class OpenFeatureClient implements Client {
      */
     @Override
     public Client on(ProviderEvent event, Consumer<EventDetails> handler) {
-        OpenFeatureAPI.getInstance().addHandler(domain, event, handler);
+        openfeatureApi.addHandler(domain, event, handler);
         return this;
     }
 
@@ -516,7 +516,7 @@ public class OpenFeatureClient implements Client {
      */
     @Override
     public Client removeHandler(ProviderEvent event, Consumer<EventDetails> handler) {
-        OpenFeatureAPI.getInstance().removeHandler(domain, event, handler);
+        openfeatureApi.removeHandler(domain, event, handler);
         return this;
     }
 }
diff --git a/src/main/java/dev/openfeature/sdk/ProviderRepository.java b/src/main/java/dev/openfeature/sdk/ProviderRepository.java
index bec8668..ab024a7 100644
--- a/src/main/java/dev/openfeature/sdk/ProviderRepository.java
+++ b/src/main/java/dev/openfeature/sdk/ProviderRepository.java
@@ -28,6 +28,11 @@ class ProviderRepository {
         return thread;
     });
     private final Object registerStateManagerLock = new Object();
+    private final OpenFeatureAPI openFeatureAPI;
+
+    public ProviderRepository(OpenFeatureAPI openFeatureAPI) {
+        this.openFeatureAPI = openFeatureAPI;
+    }
 
     FeatureProviderStateManager getFeatureProviderStateManager() {
         return defaultStateManger.get();
@@ -205,7 +210,7 @@ class ProviderRepository {
             FeatureProviderStateManager oldManager) {
         try {
             if (ProviderState.NOT_READY.equals(newManager.getState())) {
-                newManager.initialize(OpenFeatureAPI.getInstance().getEvaluationContext());
+                newManager.initialize(openFeatureAPI.getEvaluationContext());
                 afterInit.accept(newManager.getProvider());
             }
             shutDownOld(oldManager, afterShutdown);


Test patch:
diff --git a/src/test/java/dev/openfeature/sdk/AlwaysBrokenWithDetailsProvider.java b/src/test/java/dev/openfeature/sdk/AlwaysBrokenWithDetailsProvider.java
index 8f304ea..bd0ac2c 100644
--- a/src/test/java/dev/openfeature/sdk/AlwaysBrokenWithDetailsProvider.java
+++ b/src/test/java/dev/openfeature/sdk/AlwaysBrokenWithDetailsProvider.java
@@ -1,14 +1,12 @@
 package dev.openfeature.sdk;
 
-import dev.openfeature.sdk.exceptions.FlagNotFoundError;
-
 public class AlwaysBrokenWithDetailsProvider implements FeatureProvider {
 
+    private final String name = "always broken with details";
+
     @Override
     public Metadata getMetadata() {
-        return () -> {
-            throw new FlagNotFoundError(TestConstants.BROKEN_MESSAGE);
-        };
+        return () -> name;
     }
 
     @Override
diff --git a/src/test/java/dev/openfeature/sdk/AlwaysBrokenProvider.java b/src/test/java/dev/openfeature/sdk/AlwaysBrokenWithExceptionProvider.java
similarity index 94%
rename from src/test/java/dev/openfeature/sdk/AlwaysBrokenProvider.java
rename to src/test/java/dev/openfeature/sdk/AlwaysBrokenWithExceptionProvider.java
index 2f214d8..0ad09db 100644
--- a/src/test/java/dev/openfeature/sdk/AlwaysBrokenProvider.java
+++ b/src/test/java/dev/openfeature/sdk/AlwaysBrokenWithExceptionProvider.java
@@ -2,7 +2,7 @@ package dev.openfeature.sdk;
 
 import dev.openfeature.sdk.exceptions.FlagNotFoundError;
 
-public class AlwaysBrokenProvider implements FeatureProvider {
+public class AlwaysBrokenWithExceptionProvider implements FeatureProvider {
 
     private final String name = "always broken";
 
diff --git a/src/test/java/dev/openfeature/sdk/ClientProviderMappingTest.java b/src/test/java/dev/openfeature/sdk/ClientProviderMappingTest.java
index cd7e8b2..beadf7a 100644
--- a/src/test/java/dev/openfeature/sdk/ClientProviderMappingTest.java
+++ b/src/test/java/dev/openfeature/sdk/ClientProviderMappingTest.java
@@ -2,17 +2,16 @@ package dev.openfeature.sdk;
 
 import static org.junit.jupiter.api.Assertions.*;
 
-import dev.openfeature.sdk.testutils.FeatureProviderTestUtils;
 import org.junit.jupiter.api.Test;
 
 class ClientProviderMappingTest {
 
     @Test
     void clientProviderTest() {
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
+        OpenFeatureAPI api = new OpenFeatureAPI();
 
-        FeatureProviderTestUtils.setFeatureProvider("client1", new DoSomethingProvider());
-        FeatureProviderTestUtils.setFeatureProvider("client2", new NoOpProvider());
+        api.setProviderAndWait("client1", new DoSomethingProvider());
+        api.setProviderAndWait("client2", new NoOpProvider());
 
         Client c1 = api.getClient("client1");
         Client c2 = api.getClient("client2");
diff --git a/src/test/java/dev/openfeature/sdk/DeveloperExperienceTest.java b/src/test/java/dev/openfeature/sdk/DeveloperExperienceTest.java
index aacf091..32fa605 100644
--- a/src/test/java/dev/openfeature/sdk/DeveloperExperienceTest.java
+++ b/src/test/java/dev/openfeature/sdk/DeveloperExperienceTest.java
@@ -8,7 +8,6 @@ import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import dev.openfeature.sdk.fixtures.HookFixtures;
-import dev.openfeature.sdk.testutils.FeatureProviderTestUtils;
 import dev.openfeature.sdk.testutils.TestEventsProvider;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -16,14 +15,20 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import lombok.SneakyThrows;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 class DeveloperExperienceTest implements HookFixtures {
     transient String flagKey = "mykey";
+    private OpenFeatureAPI api;
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        api = new OpenFeatureAPI();
+    }
 
     @Test
     void simpleBooleanFlag() {
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
         api.setProviderAndWait(new TestEventsProvider());
         Client client = api.getClient();
         Boolean retval = client.getBooleanValue(flagKey, false);
@@ -34,7 +39,6 @@ class DeveloperExperienceTest implements HookFixtures {
     void clientHooks() {
         Hook<Boolean> exampleHook = mockBooleanHook();
 
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
         api.setProviderAndWait(new TestEventsProvider());
         Client client = api.getClient();
         client.addHooks(exampleHook);
@@ -48,7 +52,6 @@ class DeveloperExperienceTest implements HookFixtures {
         Hook<Boolean> clientHook = mockBooleanHook();
         Hook<Boolean> evalHook = mockBooleanHook();
 
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
         api.setProviderAndWait(new TestEventsProvider());
         Client client = api.getClient();
         client.addHooks(clientHook);
@@ -69,7 +72,6 @@ class DeveloperExperienceTest implements HookFixtures {
     @Test
     void providingContext() {
 
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
         api.setProviderAndWait(new TestEventsProvider());
         Client client = api.getClient();
         Map<String, Value> attributes = new HashMap<>();
@@ -86,8 +88,7 @@ class DeveloperExperienceTest implements HookFixtures {
 
     @Test
     void brokenProvider() {
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
-        FeatureProviderTestUtils.setFeatureProvider(new AlwaysBrokenProvider());
+        api.setProviderAndWait(new AlwaysBrokenWithExceptionProvider());
         Client client = api.getClient();
         FlagEvaluationDetails<Boolean> retval = client.getBooleanDetails(flagKey, false);
         assertEquals(ErrorCode.FLAG_NOT_FOUND, retval.getErrorCode());
@@ -99,6 +100,9 @@ class DeveloperExperienceTest implements HookFixtures {
     @Test
     void providerLockedPerTransaction() {
 
+        final String defaultValue = "string-value";
+        final OpenFeatureAPI api = new OpenFeatureAPI();
+
         class MutatingHook implements Hook {
 
             @Override
@@ -106,16 +110,14 @@ class DeveloperExperienceTest implements HookFixtures {
             // change the provider during a before hook - this should not impact the evaluation in progress
             public Optional before(HookContext ctx, Map hints) {
 
-                FeatureProviderTestUtils.setFeatureProvider(TestEventsProvider.newInitializedTestEventsProvider());
+                api.setProviderAndWait(TestEventsProvider.newInitializedTestEventsProvider());
 
                 return Optional.empty();
             }
         }
 
-        final String defaultValue = "string-value";
-        final OpenFeatureAPI api = OpenFeatureAPI.getInstance();
         final Client client = api.getClient();
-        FeatureProviderTestUtils.setFeatureProvider(new DoSomethingProvider());
+        api.setProviderAndWait(new DoSomethingProvider());
         api.addHooks(new MutatingHook());
 
         // if provider is changed during an evaluation transaction it should proceed with the original provider
@@ -132,7 +134,6 @@ class DeveloperExperienceTest implements HookFixtures {
     @Test
     void setProviderAndWaitShouldPutTheProviderInReadyState() {
         String domain = "domain";
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
         api.setProviderAndWait(domain, new TestEventsProvider());
         Client client = api.getClient(domain);
         assertThat(client.getProviderState()).isEqualTo(ProviderState.READY);
@@ -145,7 +146,6 @@ class DeveloperExperienceTest implements HookFixtures {
     @Test
     void shouldPutTheProviderInStateErrorAfterEmittingErrorEvent() {
         String domain = "domain";
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
         TestEventsProvider provider = new TestEventsProvider();
         api.setProviderAndWait(domain, provider);
         Client client = api.getClient(domain);
@@ -161,7 +161,6 @@ class DeveloperExperienceTest implements HookFixtures {
     @Test
     void shouldPutTheProviderInStateStaleAfterEmittingStaleEvent() {
         String domain = "domain";
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
         TestEventsProvider provider = new TestEventsProvider();
         api.setProviderAndWait(domain, provider);
         Client client = api.getClient(domain);
@@ -177,7 +176,6 @@ class DeveloperExperienceTest implements HookFixtures {
     @Test
     void shouldPutTheProviderInStateReadyAfterEmittingReadyEvent() {
         String domain = "domain";
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
         TestEventsProvider provider = new TestEventsProvider();
         api.setProviderAndWait(domain, provider);
         Client client = api.getClient(domain);
diff --git a/src/test/java/dev/openfeature/sdk/EventProviderTest.java b/src/test/java/dev/openfeature/sdk/EventProviderTest.java
index a159877..ebf8901 100644
--- a/src/test/java/dev/openfeature/sdk/EventProviderTest.java
+++ b/src/test/java/dev/openfeature/sdk/EventProviderTest.java
@@ -28,7 +28,7 @@ class EventProviderTest {
 
     @AfterAll
     public static void resetDefaultProvider() {
-        OpenFeatureAPI.getInstance().setProviderAndWait(new NoOpProvider());
+        new OpenFeatureAPI().setProviderAndWait(new NoOpProvider());
     }
 
     @Test
@@ -91,7 +91,7 @@ class EventProviderTest {
     @DisplayName("should not deadlock on emit called during emit")
     void doesNotDeadlockOnEmitStackedCalls() {
         TestStackedEmitCallsProvider provider = new TestStackedEmitCallsProvider();
-        OpenFeatureAPI.getInstance().setProviderAndWait(provider);
+        new OpenFeatureAPI().setProviderAndWait(provider);
     }
 
     static class TestEventProvider extends EventProvider {
diff --git a/src/test/java/dev/openfeature/sdk/EventsTest.java b/src/test/java/dev/openfeature/sdk/EventsTest.java
index e590246..157c0ba 100644
--- a/src/test/java/dev/openfeature/sdk/EventsTest.java
+++ b/src/test/java/dev/openfeature/sdk/EventsTest.java
@@ -7,11 +7,11 @@ import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.*;
 
 import dev.openfeature.sdk.testutils.TestEventsProvider;
-import io.cucumber.java.AfterAll;
 import java.util.Arrays;
 import java.util.List;
 import java.util.function.Consumer;
 import lombok.SneakyThrows;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -21,10 +21,11 @@ class EventsTest {
 
     private static final int TIMEOUT = 500;
     private static final int INIT_DELAY = TIMEOUT / 2;
+    private OpenFeatureAPI api;
 
-    @AfterAll
-    public static void resetDefaultProvider() {
-        OpenFeatureAPI.getInstance().setProviderAndWait(new NoOpProvider());
+    @BeforeEach
+    public void setUp() throws Exception {
+        api = new OpenFeatureAPI();
     }
 
     @Nested
@@ -49,8 +50,8 @@ class EventsTest {
                     final String name = "apiInitReady";
 
                     TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
-                    OpenFeatureAPI.getInstance().onProviderReady(handler);
-                    OpenFeatureAPI.getInstance().setProviderAndWait(name, provider);
+                    api.onProviderReady(handler);
+                    api.setProviderAndWait(name, provider);
                     verify(handler, timeout(TIMEOUT).atLeastOnce()).accept(any());
                 }
 
@@ -66,8 +67,8 @@ class EventsTest {
                     final String errMessage = "oh no!";
 
                     TestEventsProvider provider = new TestEventsProvider(INIT_DELAY, true, errMessage);
-                    OpenFeatureAPI.getInstance().onProviderError(handler);
-                    OpenFeatureAPI.getInstance().setProvider(name, provider);
+                    api.onProviderError(handler);
+                    api.setProvider(name, provider);
                     verify(handler, timeout(TIMEOUT)).accept(argThat(details -> {
                         return errMessage.equals(details.getMessage());
                     }));
@@ -89,8 +90,8 @@ class EventsTest {
                     final String name = "apiShouldPropagateEvents";
 
                     TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
-                    OpenFeatureAPI.getInstance().setProviderAndWait(name, provider);
-                    OpenFeatureAPI.getInstance().onProviderConfigurationChanged(handler);
+                    api.setProviderAndWait(name, provider);
+                    api.onProviderConfigurationChanged(handler);
 
                     provider.mockEvent(
                             ProviderEvent.PROVIDER_CONFIGURATION_CHANGED,
@@ -118,12 +119,12 @@ class EventsTest {
                     final Consumer<EventDetails> handler4 = mockHandler();
 
                     TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
-                    OpenFeatureAPI.getInstance().setProviderAndWait(name, provider);
+                    api.setProviderAndWait(name, provider);
 
-                    OpenFeatureAPI.getInstance().onProviderReady(handler1);
-                    OpenFeatureAPI.getInstance().onProviderConfigurationChanged(handler2);
-                    OpenFeatureAPI.getInstance().onProviderStale(handler3);
-                    OpenFeatureAPI.getInstance().onProviderError(handler4);
+                    api.onProviderReady(handler1);
+                    api.onProviderConfigurationChanged(handler2);
+                    api.onProviderStale(handler3);
+                    api.onProviderError(handler4);
 
                     Arrays.asList(ProviderEvent.values()).stream().forEach(eventType -> {
                         provider.mockEvent(
@@ -162,8 +163,8 @@ class EventsTest {
 
                     TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
                     // set provider before getting a client
-                    OpenFeatureAPI.getInstance().setProviderAndWait(provider);
-                    Client client = OpenFeatureAPI.getInstance().getClient();
+                    api.setProviderAndWait(provider);
+                    Client client = api.getClient();
                     client.onProviderStale(handler);
 
                     provider.mockEvent(
@@ -183,8 +184,8 @@ class EventsTest {
 
                     TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
                     // set provider before getting a client
-                    OpenFeatureAPI.getInstance().setProviderAndWait(provider);
-                    Client client = OpenFeatureAPI.getInstance().getClient(name);
+                    api.setProviderAndWait(provider);
+                    Client client = api.getClient(name);
                     client.onProviderStale(handler);
 
                     provider.mockEvent(
@@ -213,10 +214,10 @@ class EventsTest {
                 final String name = "initReadyProviderBefore";
 
                 TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
-                Client client = OpenFeatureAPI.getInstance().getClient(name);
+                Client client = api.getClient(name);
                 client.onProviderReady(handler);
                 // set provider after getting a client
-                OpenFeatureAPI.getInstance().setProviderAndWait(name, provider);
+                api.setProviderAndWait(name, provider);
                 verify(handler, timeout(TIMEOUT).atLeastOnce())
                         .accept(argThat(details -> details.getDomain().equals(name)));
             }
@@ -233,8 +234,8 @@ class EventsTest {
 
                 TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
                 // set provider before getting a client
-                OpenFeatureAPI.getInstance().setProviderAndWait(name, provider);
-                Client client = OpenFeatureAPI.getInstance().getClient(name);
+                api.setProviderAndWait(name, provider);
+                Client client = api.getClient(name);
                 client.onProviderReady(handler);
                 verify(handler, timeout(TIMEOUT).atLeastOnce())
                         .accept(argThat(details -> details.getDomain().equals(name)));
@@ -252,10 +253,10 @@ class EventsTest {
                 final String errMessage = "oh no!";
 
                 TestEventsProvider provider = new TestEventsProvider(INIT_DELAY, true, errMessage);
-                Client client = OpenFeatureAPI.getInstance().getClient(name);
+                Client client = api.getClient(name);
                 client.onProviderError(handler);
                 // set provider after getting a client
-                OpenFeatureAPI.getInstance().setProvider(name, provider);
+                api.setProvider(name, provider);
                 verify(handler, timeout(TIMEOUT)).accept(argThat(details -> {
                     return name.equals(details.getDomain()) && errMessage.equals(details.getMessage());
                 }));
@@ -274,8 +275,8 @@ class EventsTest {
 
                 TestEventsProvider provider = new TestEventsProvider(INIT_DELAY, true, errMessage);
                 // set provider after getting a client
-                OpenFeatureAPI.getInstance().setProvider(name, provider);
-                Client client = OpenFeatureAPI.getInstance().getClient(name);
+                api.setProvider(name, provider);
+                Client client = api.getClient(name);
                 client.onProviderError(handler);
                 verify(handler, timeout(TIMEOUT)).accept(argThat(details -> {
                     return name.equals(details.getDomain()) && errMessage.equals(details.getMessage());
@@ -299,8 +300,8 @@ class EventsTest {
 
                 TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
                 // set provider before getting a client
-                OpenFeatureAPI.getInstance().setProviderAndWait(name, provider);
-                Client client = OpenFeatureAPI.getInstance().getClient(name);
+                api.setProviderAndWait(name, provider);
+                Client client = api.getClient(name);
                 client.onProviderConfigurationChanged(handler);
 
                 provider.mockEvent(
@@ -322,10 +323,10 @@ class EventsTest {
                 final String name = "shouldPropagateAfter";
 
                 TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
-                Client client = OpenFeatureAPI.getInstance().getClient(name);
+                Client client = api.getClient(name);
                 client.onProviderConfigurationChanged(handler);
                 // set provider after getting a client
-                OpenFeatureAPI.getInstance().setProviderAndWait(name, provider);
+                api.setProviderAndWait(name, provider);
 
                 provider.mockEvent(
                         ProviderEvent.PROVIDER_CONFIGURATION_CHANGED,
@@ -354,8 +355,8 @@ class EventsTest {
                 final Consumer<EventDetails> handler4 = mockHandler();
 
                 TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
-                OpenFeatureAPI.getInstance().setProviderAndWait(name, provider);
-                Client client = OpenFeatureAPI.getInstance().getClient(name);
+                api.setProviderAndWait(name, provider);
+                Client client = api.getClient(name);
 
                 client.onProviderReady(handler1);
                 client.onProviderConfigurationChanged(handler2);
@@ -384,14 +385,14 @@ class EventsTest {
 
         TestEventsProvider provider1 = new TestEventsProvider(INIT_DELAY);
         TestEventsProvider provider2 = new TestEventsProvider(INIT_DELAY);
-        OpenFeatureAPI.getInstance().setProviderAndWait(name, provider1);
-        Client client = OpenFeatureAPI.getInstance().getClient(name);
+        api.setProviderAndWait(name, provider1);
+        Client client = api.getClient(name);
 
         // attached handlers
-        OpenFeatureAPI.getInstance().onProviderConfigurationChanged(handler1);
+        api.onProviderConfigurationChanged(handler1);
         client.onProviderConfigurationChanged(handler2);
 
-        OpenFeatureAPI.getInstance().setProviderAndWait(name, provider2);
+        api.setProviderAndWait(name, provider2);
 
         // wait for the new provider to be ready and make sure things are cleaned up.
         await().until(() -> provider1.isShutDown());
@@ -421,11 +422,11 @@ class EventsTest {
 
         TestEventsProvider provider1 = new TestEventsProvider(INIT_DELAY);
         TestEventsProvider provider2 = new TestEventsProvider(INIT_DELAY);
-        OpenFeatureAPI.getInstance().setProviderAndWait(name1, provider1);
-        OpenFeatureAPI.getInstance().setProviderAndWait(name2, provider2);
+        api.setProviderAndWait(name1, provider1);
+        api.setProviderAndWait(name2, provider2);
 
-        Client client1 = OpenFeatureAPI.getInstance().getClient(name1);
-        Client client2 = OpenFeatureAPI.getInstance().getClient(name2);
+        Client client1 = api.getClient(name1);
+        Client client2 = api.getClient(name2);
 
         client1.onProviderConfigurationChanged(handlerToRun);
         client2.onProviderConfigurationChanged(handlerNotToRun);
@@ -450,11 +451,11 @@ class EventsTest {
 
         TestEventsProvider namedProvider = new TestEventsProvider(INIT_DELAY);
         TestEventsProvider defaultProvider = new TestEventsProvider(INIT_DELAY);
-        OpenFeatureAPI.getInstance().setProviderAndWait(defaultProvider);
+        api.setProviderAndWait(defaultProvider);
 
-        Client client = OpenFeatureAPI.getInstance().getClient(name);
+        Client client = api.getClient(name);
         client.onProviderConfigurationChanged(handlerNotToRun);
-        OpenFeatureAPI.getInstance().setProviderAndWait(name, namedProvider);
+        api.setProviderAndWait(name, namedProvider);
 
         // await the new provider to make sure the old one is shut down
         await().until(() -> namedProvider.getState().equals(ProviderState.READY));
@@ -465,7 +466,7 @@ class EventsTest {
                 ProviderEventDetails.builder().build());
 
         verify(handlerNotToRun, after(TIMEOUT).never()).accept(any());
-        OpenFeatureAPI.getInstance().setProviderAndWait(new NoOpProvider());
+        api.setProviderAndWait(new NoOpProvider());
     }
 
     @Test
@@ -479,9 +480,9 @@ class EventsTest {
         final Consumer<EventDetails> handlerToRun = mockHandler();
 
         TestEventsProvider defaultProvider = new TestEventsProvider(INIT_DELAY);
-        OpenFeatureAPI.getInstance().setProviderAndWait(defaultProvider);
+        api.setProviderAndWait(defaultProvider);
 
-        Client client = OpenFeatureAPI.getInstance().getClient(name);
+        Client client = api.getClient(name);
         client.onProviderConfigurationChanged(handlerToRun);
 
         // await the new provider to make sure the old one is shut down
@@ -493,7 +494,7 @@ class EventsTest {
                 ProviderEventDetails.builder().build());
 
         verify(handlerToRun, timeout(TIMEOUT)).accept(any());
-        OpenFeatureAPI.getInstance().setProviderAndWait(new NoOpProvider());
+        api.setProviderAndWait(new NoOpProvider());
     }
 
     @Test
@@ -509,9 +510,9 @@ class EventsTest {
         final Consumer<EventDetails> lastHandler = mockHandler();
 
         TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
-        OpenFeatureAPI.getInstance().setProviderAndWait(name, provider);
+        api.setProviderAndWait(name, provider);
 
-        Client client1 = OpenFeatureAPI.getInstance().getClient(name);
+        Client client1 = api.getClient(name);
 
         client1.onProviderConfigurationChanged(errorHandler);
         client1.onProviderConfigurationChanged(nextHandler);
@@ -537,11 +538,11 @@ class EventsTest {
         final String name = "shouldHaveAllProperties";
 
         TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
-        OpenFeatureAPI.getInstance().setProviderAndWait(name, provider);
-        Client client = OpenFeatureAPI.getInstance().getClient(name);
+        api.setProviderAndWait(name, provider);
+        Client client = api.getClient(name);
 
         // attached handlers
-        OpenFeatureAPI.getInstance().onProviderConfigurationChanged(handler1);
+        api.onProviderConfigurationChanged(handler1);
         client.onProviderConfigurationChanged(handler2);
 
         List<String> flagsChanged = Arrays.asList("flag");
@@ -582,10 +583,10 @@ class EventsTest {
 
         // provider which is already ready
         TestEventsProvider provider = new TestEventsProvider();
-        OpenFeatureAPI.getInstance().setProviderAndWait(name, provider);
+        api.setProviderAndWait(name, provider);
 
         // should run even thought handler was added after ready
-        Client client = OpenFeatureAPI.getInstance().getClient(name);
+        Client client = api.getClient(name);
         client.onProviderReady(handler);
         verify(handler, timeout(TIMEOUT)).accept(any());
     }
@@ -598,7 +599,6 @@ class EventsTest {
     void matchingStaleEventsMustRunImmediately() {
         final String name = "matchingEventsMustRunImmediately";
         final Consumer<EventDetails> handler = mockHandler();
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
 
         // provider which is already stale
         TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
@@ -620,7 +620,6 @@ class EventsTest {
     void matchingErrorEventsMustRunImmediately() {
         final String name = "matchingEventsMustRunImmediately";
         final Consumer<EventDetails> handler = mockHandler();
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
 
         // provider which is already in error
         TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
@@ -629,6 +628,7 @@ class EventsTest {
         provider.emitProviderError(ProviderEventDetails.builder().build());
         assertThat(client.getProviderState()).isEqualTo(ProviderState.ERROR);
 
+        verify(handler, never()).accept(any());
         // should run even though handler was added after error
         client.onProviderError(handler);
         verify(handler, timeout(TIMEOUT)).accept(any());
@@ -644,8 +644,8 @@ class EventsTest {
         TestEventsProvider provider1 = new TestEventsProvider(INIT_DELAY);
         TestEventsProvider provider2 = new TestEventsProvider(INIT_DELAY);
 
-        OpenFeatureAPI.getInstance().setProviderAndWait(name, provider1);
-        Client client = OpenFeatureAPI.getInstance().getClient(name);
+        api.setProviderAndWait(name, provider1);
+        Client client = api.getClient(name);
         client.onProviderConfigurationChanged(handler);
 
         provider1.mockEvent(
@@ -657,7 +657,7 @@ class EventsTest {
         verify(handler, timeout(TIMEOUT).times(1)).accept(argThat(nameMatches));
 
         // wait for the new provider to be ready.
-        OpenFeatureAPI.getInstance().setProviderAndWait(name, provider2);
+        api.setProviderAndWait(name, provider2);
 
         // verify that with the new provider under the same name, the handler is called
         // again.
@@ -681,14 +681,14 @@ class EventsTest {
             final Consumer<EventDetails> handler2 = mockHandler();
 
             TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
-            OpenFeatureAPI.getInstance().setProviderAndWait(name, provider);
-            Client client = OpenFeatureAPI.getInstance().getClient(name);
+            api.setProviderAndWait(name, provider);
+            Client client = api.getClient(name);
 
             // attached handlers
-            OpenFeatureAPI.getInstance().onProviderStale(handler1);
+            api.onProviderStale(handler1);
             client.onProviderConfigurationChanged(handler2);
 
-            OpenFeatureAPI.getInstance().removeHandler(ProviderEvent.PROVIDER_STALE, handler1);
+            api.removeHandler(ProviderEvent.PROVIDER_STALE, handler1);
             client.removeHandler(ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, handler2);
 
             // emit event
diff --git a/src/test/java/dev/openfeature/sdk/FlagEvaluationSpecTest.java b/src/test/java/dev/openfeature/sdk/FlagEvaluationSpecTest.java
index 2ad88d3..3b02b17 100644
--- a/src/test/java/dev/openfeature/sdk/FlagEvaluationSpecTest.java
+++ b/src/test/java/dev/openfeature/sdk/FlagEvaluationSpecTest.java
@@ -9,7 +9,6 @@ import static org.mockito.Mockito.*;
 
 import dev.openfeature.sdk.exceptions.GeneralError;
 import dev.openfeature.sdk.fixtures.HookFixtures;
-import dev.openfeature.sdk.testutils.FeatureProviderTestUtils;
 import dev.openfeature.sdk.testutils.TestEventsProvider;
 import java.util.HashMap;
 import java.util.List;
@@ -29,7 +28,7 @@ class FlagEvaluationSpecTest implements HookFixtures {
     private OpenFeatureAPI api;
 
     private Client _client() {
-        FeatureProviderTestUtils.setFeatureProvider(new NoOpProvider());
+        api.setProviderAndWait(new NoOpProvider());
         return api.getClient();
     }
 
@@ -37,18 +36,13 @@ class FlagEvaluationSpecTest implements HookFixtures {
     private Client _initializedClient() {
         TestEventsProvider provider = new TestEventsProvider();
         provider.initialize(null);
-        FeatureProviderTestUtils.setFeatureProvider(provider);
+        api.setProviderAndWait(provider);
         return api.getClient();
     }
 
     @BeforeEach
     void getApiInstance() {
-        api = OpenFeatureAPI.getInstance();
-    }
-
-    @AfterEach
-    void reset_ctx() {
-        api.setEvaluationContext(null);
+        api = new OpenFeatureAPI();
     }
 
     @BeforeEach
@@ -62,15 +56,6 @@ class FlagEvaluationSpecTest implements HookFixtures {
         LoggerMock.setMock(OpenFeatureClient.class, logger);
     }
 
-    @Specification(
-            number = "1.1.1",
-            text =
-                    "The API, and any state it maintains SHOULD exist as a global singleton, even in cases wherein multiple versions of the API are present at runtime.")
-    @Test
-    void global_singleton() {
-        assertSame(OpenFeatureAPI.getInstance(), OpenFeatureAPI.getInstance());
-    }
-
     @Specification(
             number = "1.1.2.1",
             text =
@@ -78,7 +63,7 @@ class FlagEvaluationSpecTest implements HookFixtures {
     @Test
     void provider() {
         FeatureProvider mockProvider = mock(FeatureProvider.class);
-        FeatureProviderTestUtils.setFeatureProvider(mockProvider);
+        api.setProviderAndWait(mockProvider);
         assertThat(api.getProvider()).isEqualTo(mockProvider);
     }
 
@@ -90,13 +75,13 @@ class FlagEvaluationSpecTest implements HookFixtures {
     @Test
     void providerAndWait() {
         FeatureProvider provider = new TestEventsProvider(500);
-        OpenFeatureAPI.getInstance().setProviderAndWait(provider);
+        api.setProviderAndWait(provider);
         Client client = api.getClient();
         assertThat(client.getProviderState()).isEqualTo(ProviderState.READY);
 
         provider = new TestEventsProvider(500);
         String providerName = "providerAndWait";
-        OpenFeatureAPI.getInstance().setProviderAndWait(providerName, provider);
+        api.setProviderAndWait(providerName, provider);
         Client client2 = api.getClient(providerName);
         assertThat(client2.getProviderState()).isEqualTo(ProviderState.READY);
     }
@@ -124,8 +109,8 @@ class FlagEvaluationSpecTest implements HookFixtures {
     void shouldReturnNotReadyIfNotInitialized() {
         FeatureProvider provider = new TestEventsProvider(100);
         String providerName = "shouldReturnNotReadyIfNotInitialized";
-        OpenFeatureAPI.getInstance().setProvider(providerName, provider);
-        Client client = OpenFeatureAPI.getInstance().getClient(providerName);
+        api.setProvider(providerName, provider);
+        Client client = api.getClient(providerName);
         FlagEvaluationDetails<Boolean> details = client.getBooleanDetails("return_error_when_not_initialized", false);
         assertEquals(ErrorCode.PROVIDER_NOT_READY, details.getErrorCode());
         assertEquals(Reason.ERROR.toString(), details.getReason());
@@ -136,7 +121,7 @@ class FlagEvaluationSpecTest implements HookFixtures {
             text = "The API MUST provide a function for retrieving the metadata field of the configured provider.")
     @Test
     void provider_metadata() {
-        FeatureProviderTestUtils.setFeatureProvider(new DoSomethingProvider());
+        api.setProviderAndWait(new DoSomethingProvider());
         assertThat(api.getProviderMetadata().getName()).isEqualTo(DoSomethingProvider.name);
     }
 
@@ -198,7 +183,7 @@ class FlagEvaluationSpecTest implements HookFixtures {
                     "The client SHOULD provide functions for floating-point numbers and integers, consistent with language idioms.")
     @Test
     void value_flags() {
-        FeatureProviderTestUtils.setFeatureProvider(new DoSomethingProvider());
+        api.setProviderAndWait(new DoSomethingProvider());
 
         Client c = api.getClient();
         String key = "key";
@@ -279,7 +264,7 @@ class FlagEvaluationSpecTest implements HookFixtures {
                     "In cases of normal execution, the `evaluation details` structure's `reason` field MUST contain the value of the `reason` field in the `flag resolution` structure returned by the configured `provider`, if the field is set.")
     @Test
     void detail_flags() {
-        FeatureProviderTestUtils.setFeatureProvider(new DoSomethingProvider());
+        api.setProviderAndWait(new DoSomethingProvider());
         Client c = api.getClient();
         String key = "key";
 
@@ -386,7 +371,7 @@ class FlagEvaluationSpecTest implements HookFixtures {
                     "In cases of abnormal execution, the `evaluation details` structure's `error message` field **MAY** contain a string containing additional details about the nature of the error.")
     @Test
     void broken_provider() {
-        FeatureProviderTestUtils.setFeatureProvider(new AlwaysBrokenProvider());
+        api.setProviderAndWait(new AlwaysBrokenWithExceptionProvider());
         Client c = api.getClient();
         boolean defaultValue = false;
         assertFalse(c.getBooleanValue("key", defaultValue));
@@ -414,8 +399,8 @@ class FlagEvaluationSpecTest implements HookFixtures {
             text =
                     "In cases of abnormal execution, the `evaluation details` structure's `error message` field **MAY** contain a string containing additional details about the nature of the error.")
     @Test
-    void broken_provider_withDetails() {
-        FeatureProviderTestUtils.setFeatureProvider(new AlwaysBrokenWithDetailsProvider());
+    void broken_provider_withDetails() throws InterruptedException {
+        api.setProviderAndWait(new AlwaysBrokenWithDetailsProvider());
         Client c = api.getClient();
         boolean defaultValue = false;
         assertFalse(c.getBooleanValue("key", defaultValue));
@@ -431,7 +416,7 @@ class FlagEvaluationSpecTest implements HookFixtures {
             text = "Methods, functions, or operations on the client SHOULD NOT write log messages.")
     @Test
     void log_on_error() throws NotImplementedException {
-        FeatureProviderTestUtils.setFeatureProvider(new AlwaysBrokenProvider());
+        api.setProviderAndWait(new AlwaysBrokenWithExceptionProvider());
         Client c = api.getClient();
         FlagEvaluationDetails<Boolean> result = c.getBooleanDetails("test", false);
 
@@ -450,7 +435,7 @@ class FlagEvaluationSpecTest implements HookFixtures {
         assertNull(c.getMetadata().getDomain());
 
         String domainName = "test domain";
-        FeatureProviderTestUtils.setFeatureProvider(new AlwaysBrokenProvider());
+        api.setProviderAndWait(new AlwaysBrokenWithExceptionProvider());
         Client c2 = api.getClient(domainName);
 
         assertEquals(domainName, c2.getMetadata().getName());
@@ -463,7 +448,7 @@ class FlagEvaluationSpecTest implements HookFixtures {
                     "In cases of abnormal execution (network failure, unhandled error, etc) the reason field in the evaluation details SHOULD indicate an error.")
     @Test
     void reason_is_error_when_there_are_errors() {
-        FeatureProviderTestUtils.setFeatureProvider(new AlwaysBrokenProvider());
+        api.setProviderAndWait(new AlwaysBrokenWithExceptionProvider());
         Client c = api.getClient();
         FlagEvaluationDetails<Boolean> result = c.getBooleanDetails("test", false);
         assertEquals(Reason.ERROR.toString(), result.getReason());
@@ -475,7 +460,7 @@ class FlagEvaluationSpecTest implements HookFixtures {
                     "If the flag metadata field in the flag resolution structure returned by the configured provider is set, the evaluation details structure's flag metadata field MUST contain that value. Otherwise, it MUST contain an empty record.")
     @Test
     void flag_metadata_passed() {
-        FeatureProviderTestUtils.setFeatureProvider(new DoSomethingProvider(null));
+        api.setProviderAndWait(new DoSomethingProvider(null));
         Client c = api.getClient();
         FlagEvaluationDetails<Boolean> result = c.getBooleanDetails("test", false);
         assertNotNull(result.getFlagMetadata());
@@ -487,7 +472,7 @@ class FlagEvaluationSpecTest implements HookFixtures {
         String contextKey = "some-key";
         String contextValue = "some-value";
         DoSomethingProvider provider = spy(new DoSomethingProvider());
-        FeatureProviderTestUtils.setFeatureProvider(provider);
+        api.setProviderAndWait(provider);
 
         Map<String, Value> attributes = new HashMap<>();
         attributes.put(contextKey, new Value(contextValue));
@@ -514,7 +499,7 @@ class FlagEvaluationSpecTest implements HookFixtures {
     @Test
     void multi_layer_context_merges_correctly() {
         DoSomethingProvider provider = spy(new DoSomethingProvider());
-        FeatureProviderTestUtils.setFeatureProvider(provider);
+        api.setProviderAndWait(provider);
         TransactionContextPropagator transactionContextPropagator = new ThreadLocalTransactionContextPropagator();
         api.setTransactionContextPropagator(transactionContextPropagator);
         Hook<Boolean> hook = spy(new Hook<Boolean>() {
@@ -702,7 +687,7 @@ class FlagEvaluationSpecTest implements HookFixtures {
     @Test
     void setting_transaction_context_propagator() {
         DoSomethingProvider provider = new DoSomethingProvider();
-        FeatureProviderTestUtils.setFeatureProvider(provider);
+        api.setProviderAndWait(provider);
 
         TransactionContextPropagator transactionContextPropagator = new ThreadLocalTransactionContextPropagator();
         api.setTransactionContextPropagator(transactionContextPropagator);
@@ -716,7 +701,7 @@ class FlagEvaluationSpecTest implements HookFixtures {
     @Test
     void setting_transaction_context() {
         DoSomethingProvider provider = new DoSomethingProvider();
-        FeatureProviderTestUtils.setFeatureProvider(provider);
+        api.setProviderAndWait(provider);
 
         TransactionContextPropagator transactionContextPropagator = new ThreadLocalTransactionContextPropagator();
         api.setTransactionContextPropagator(transactionContextPropagator);
diff --git a/src/test/java/dev/openfeature/sdk/HookSpecTest.java b/src/test/java/dev/openfeature/sdk/HookSpecTest.java
index d6247c6..4b08510 100644
--- a/src/test/java/dev/openfeature/sdk/HookSpecTest.java
+++ b/src/test/java/dev/openfeature/sdk/HookSpecTest.java
@@ -18,7 +18,6 @@ import static org.mockito.Mockito.when;
 
 import dev.openfeature.sdk.exceptions.FlagNotFoundError;
 import dev.openfeature.sdk.fixtures.HookFixtures;
-import dev.openfeature.sdk.testutils.FeatureProviderTestUtils;
 import dev.openfeature.sdk.testutils.TestEventsProvider;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -28,16 +27,18 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import lombok.SneakyThrows;
-import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 
 class HookSpecTest implements HookFixtures {
-    @AfterEach
-    void emptyApiHooks() {
-        // it's a singleton. Don't pollute each test.
-        OpenFeatureAPI.getInstance().clearHooks();
+
+    private OpenFeatureAPI api;
+
+    @BeforeEach
+    void setUp() {
+        this.api = new OpenFeatureAPI();
     }
 
     @Specification(
@@ -163,7 +164,7 @@ class HookSpecTest implements HookFixtures {
                 .type(FlagValueType.INTEGER)
                 .ctx(new ImmutableContext())
                 .defaultValue(1)
-                .clientMetadata(OpenFeatureAPI.getInstance().getClient().getMetadata())
+                .clientMetadata(api.getClient().getMetadata())
                 .build();
     }
 
@@ -173,8 +174,8 @@ class HookSpecTest implements HookFixtures {
                     "The before stage MUST run before flag resolution occurs. It accepts a hook context (required) and hook hints (optional) as parameters and returns either an evaluation context or nothing.")
     @Test
     void before_runs_ahead_of_evaluation() {
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
-        api.setProviderAndWait(new AlwaysBrokenProvider());
+
+        api.setProviderAndWait(new AlwaysBrokenWithExceptionProvider());
         Client client = api.getClient();
         Hook<Boolean> evalHook = mockBooleanHook();
 
@@ -216,8 +217,7 @@ class HookSpecTest implements HookFixtures {
                         .errorMessage(errorMessage)
                         .build());
 
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
-        FeatureProviderTestUtils.setFeatureProvider("errorHookMustRun", provider);
+        api.setProviderAndWait("errorHookMustRun", provider);
         Client client = api.getClient("errorHookMustRun");
         client.getBooleanValue(
                 "key",
@@ -259,7 +259,7 @@ class HookSpecTest implements HookFixtures {
     @Test
     void hook_eval_order() {
         List<String> evalOrder = new ArrayList<>();
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
+
         api.setProviderAndWait("evalOrder", new TestEventsProvider() {
             public List<Hook> getProviderHooks() {
                 return Collections.singletonList(new BooleanHook() {
@@ -411,8 +411,7 @@ class HookSpecTest implements HookFixtures {
         doThrow(RuntimeException.class).when(h).before(any(), any());
         Hook<Boolean> h2 = mockBooleanHook();
 
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
-        api.setProviderAndWait(new AlwaysBrokenProvider());
+        api.setProviderAndWait(new AlwaysBrokenWithExceptionProvider());
         Client c = api.getClient();
 
         c.getBooleanDetails(
@@ -516,8 +515,7 @@ class HookSpecTest implements HookFixtures {
                 .thenReturn(ProviderEvaluation.<Boolean>builder().value(true).build());
         InOrder order = inOrder(hook, provider);
 
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
-        FeatureProviderTestUtils.setFeatureProvider(provider);
+        api.setProviderAndWait(provider);
         Client client = api.getClient();
         client.getBooleanValue(
                 "key",
@@ -695,8 +693,7 @@ class HookSpecTest implements HookFixtures {
         when(provider.getBooleanEvaluation(any(), any(), any()))
                 .thenReturn(ProviderEvaluation.<Boolean>builder().value(true).build());
 
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
-        FeatureProviderTestUtils.setFeatureProvider(provider);
+        api.setProviderAndWait(provider);
         Client client = api.getClient();
         client.getBooleanValue(
                 "key",
@@ -761,11 +758,10 @@ class HookSpecTest implements HookFixtures {
     }
 
     private Client getClient(FeatureProvider provider) {
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
         if (provider == null) {
-            FeatureProviderTestUtils.setFeatureProvider(TestEventsProvider.newInitializedTestEventsProvider());
+            api.setProviderAndWait(TestEventsProvider.newInitializedTestEventsProvider());
         } else {
-            FeatureProviderTestUtils.setFeatureProvider(provider);
+            api.setProviderAndWait(provider);
         }
         return api.getClient();
     }
diff --git a/src/test/java/dev/openfeature/sdk/InitializeBehaviorSpecTest.java b/src/test/java/dev/openfeature/sdk/InitializeBehaviorSpecTest.java
index 3353f56..4bcd731 100644
--- a/src/test/java/dev/openfeature/sdk/InitializeBehaviorSpecTest.java
+++ b/src/test/java/dev/openfeature/sdk/InitializeBehaviorSpecTest.java
@@ -17,10 +17,12 @@ import org.junit.jupiter.api.Test;
 class InitializeBehaviorSpecTest {
 
     private static final String DOMAIN_NAME = "mydomain";
+    private OpenFeatureAPI api;
 
     @BeforeEach
     void setupTest() {
-        OpenFeatureAPI.getInstance().setProvider(new NoOpProvider());
+        this.api = new OpenFeatureAPI();
+        api.setProvider(new NoOpProvider());
     }
 
     @Nested
@@ -37,7 +39,7 @@ class InitializeBehaviorSpecTest {
             FeatureProvider featureProvider = mock(FeatureProvider.class);
             doReturn(ProviderState.NOT_READY).when(featureProvider).getState();
 
-            OpenFeatureAPI.getInstance().setProvider(featureProvider);
+            api.setProvider(featureProvider);
 
             verify(featureProvider, timeout(1000)).initialize(any());
         }
@@ -55,8 +57,7 @@ class InitializeBehaviorSpecTest {
             doReturn(ProviderState.NOT_READY).when(featureProvider).getState();
             doThrow(TestException.class).when(featureProvider).initialize(any());
 
-            assertThatCode(() -> OpenFeatureAPI.getInstance().setProvider(featureProvider))
-                    .doesNotThrowAnyException();
+            assertThatCode(() -> api.setProvider(featureProvider)).doesNotThrowAnyException();
 
             verify(featureProvider, timeout(1000)).initialize(any());
         }
@@ -77,7 +78,7 @@ class InitializeBehaviorSpecTest {
             FeatureProvider featureProvider = mock(FeatureProvider.class);
             doReturn(ProviderState.NOT_READY).when(featureProvider).getState();
 
-            OpenFeatureAPI.getInstance().setProvider(DOMAIN_NAME, featureProvider);
+            api.setProvider(DOMAIN_NAME, featureProvider);
 
             verify(featureProvider, timeout(1000)).initialize(any());
         }
@@ -95,8 +96,7 @@ class InitializeBehaviorSpecTest {
             doReturn(ProviderState.NOT_READY).when(featureProvider).getState();
             doThrow(TestException.class).when(featureProvider).initialize(any());
 
-            assertThatCode(() -> OpenFeatureAPI.getInstance().setProvider(DOMAIN_NAME, featureProvider))
-                    .doesNotThrowAnyException();
+            assertThatCode(() -> api.setProvider(DOMAIN_NAME, featureProvider)).doesNotThrowAnyException();
 
             verify(featureProvider, timeout(1000)).initialize(any());
         }
diff --git a/src/test/java/dev/openfeature/sdk/LockingTest.java b/src/test/java/dev/openfeature/sdk/LockingSingeltonTest.java
similarity index 99%
rename from src/test/java/dev/openfeature/sdk/LockingTest.java
rename to src/test/java/dev/openfeature/sdk/LockingSingeltonTest.java
index 4b7af55..ad86f4b 100644
--- a/src/test/java/dev/openfeature/sdk/LockingTest.java
+++ b/src/test/java/dev/openfeature/sdk/LockingSingeltonTest.java
@@ -15,7 +15,7 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.parallel.Isolated;
 
 @Isolated()
-class LockingTest {
+class LockingSingeltonTest {
 
     private static OpenFeatureAPI api;
     private OpenFeatureClient client;
diff --git a/src/test/java/dev/openfeature/sdk/OpenFeatureAPISingeltonTest.java b/src/test/java/dev/openfeature/sdk/OpenFeatureAPISingeltonTest.java
new file mode 100644
index 0000000..dd9916e
--- /dev/null
+++ b/src/test/java/dev/openfeature/sdk/OpenFeatureAPISingeltonTest.java
@@ -0,0 +1,17 @@
+package dev.openfeature.sdk;
+
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+import org.junit.jupiter.api.Test;
+
+class OpenFeatureAPISingeltonTest {
+
+    @Specification(
+            number = "1.1.1",
+            text =
+                    "The API, and any state it maintains SHOULD exist as a global singleton, even in cases wherein multiple versions of the API are present at runtime.")
+    @Test
+    void global_singleton() {
+        assertSame(OpenFeatureAPI.getInstance(), OpenFeatureAPI.getInstance());
+    }
+}
diff --git a/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java b/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java
index 63145ec..e8e8b27 100644
--- a/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java
+++ b/src/test/java/dev/openfeature/sdk/OpenFeatureAPITest.java
@@ -8,7 +8,6 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
 import dev.openfeature.sdk.providers.memory.InMemoryProvider;
-import dev.openfeature.sdk.testutils.FeatureProviderTestUtils;
 import dev.openfeature.sdk.testutils.TestEventsProvider;
 import java.util.Collections;
 import java.util.HashMap;
@@ -23,13 +22,13 @@ class OpenFeatureAPITest {
 
     @BeforeEach
     void setupTest() {
-        api = OpenFeatureAPI.getInstance();
+        api = new OpenFeatureAPI();
     }
 
     @Test
     void namedProviderTest() {
         FeatureProvider provider = new NoOpProvider();
-        FeatureProviderTestUtils.setFeatureProvider("namedProviderTest", provider);
+        api.setProviderAndWait("namedProviderTest", provider);
 
         assertThat(provider.getMetadata().getName())
                 .isEqualTo(api.getProviderMetadata("namedProviderTest").getName());
@@ -44,14 +43,10 @@ class OpenFeatureAPITest {
         String domain = "namedProviderOverwrittenTest";
         FeatureProvider provider1 = new NoOpProvider();
         FeatureProvider provider2 = new DoSomethingProvider();
-        FeatureProviderTestUtils.setFeatureProvider(domain, provider1);
-        FeatureProviderTestUtils.setFeatureProvider(domain, provider2);
-
-        assertThat(OpenFeatureAPI.getInstance()
-                        .getProvider(domain)
-                        .getMetadata()
-                        .getName())
-                .isEqualTo(DoSomethingProvider.name);
+        api.setProviderAndWait(domain, provider1);
+        api.setProviderAndWait(domain, provider2);
+
+        assertThat(api.getProvider(domain).getMetadata().getName()).isEqualTo(DoSomethingProvider.name);
     }
 
     @Test
@@ -60,17 +55,17 @@ class OpenFeatureAPITest {
         FeatureProvider noOpAsNonEventingProvider = new NoOpProvider();
 
         // register same provider for multiple names & as default provider
-        OpenFeatureAPI.getInstance().setProviderAndWait(inMemAsEventingProvider);
-        OpenFeatureAPI.getInstance().setProviderAndWait("clientA", inMemAsEventingProvider);
-        OpenFeatureAPI.getInstance().setProviderAndWait("clientB", inMemAsEventingProvider);
-        OpenFeatureAPI.getInstance().setProviderAndWait("clientC", noOpAsNonEventingProvider);
-        OpenFeatureAPI.getInstance().setProviderAndWait("clientD", noOpAsNonEventingProvider);
-
-        assertEquals(inMemAsEventingProvider, OpenFeatureAPI.getInstance().getProvider());
-        assertEquals(inMemAsEventingProvider, OpenFeatureAPI.getInstance().getProvider("clientA"));
-        assertEquals(inMemAsEventingProvider, OpenFeatureAPI.getInstance().getProvider("clientB"));
-        assertEquals(noOpAsNonEventingProvider, OpenFeatureAPI.getInstance().getProvider("clientC"));
-        assertEquals(noOpAsNonEventingProvider, OpenFeatureAPI.getInstance().getProvider("clientD"));
+        api.setProviderAndWait(inMemAsEventingProvider);
+        api.setProviderAndWait("clientA", inMemAsEventingProvider);
+        api.setProviderAndWait("clientB", inMemAsEventingProvider);
+        api.setProviderAndWait("clientC", noOpAsNonEventingProvider);
+        api.setProviderAndWait("clientD", noOpAsNonEventingProvider);
+
+        assertEquals(inMemAsEventingProvider, api.getProvider());
+        assertEquals(inMemAsEventingProvider, api.getProvider("clientA"));
+        assertEquals(inMemAsEventingProvider, api.getProvider("clientB"));
+        assertEquals(noOpAsNonEventingProvider, api.getProvider("clientC"));
+        assertEquals(noOpAsNonEventingProvider, api.getProvider("clientD"));
     }
 
     @Test
@@ -101,23 +96,20 @@ class OpenFeatureAPITest {
         String domain = "namedProviderOverwrittenTest";
         FeatureProvider provider1 = new NoOpProvider();
         FeatureProvider provider2 = new TestEventsProvider();
-        FeatureProviderTestUtils.setFeatureProvider(domain, provider1);
-        FeatureProviderTestUtils.setFeatureProvider(domain, provider2);
+        api.setProviderAndWait(domain, provider1);
+        api.setProviderAndWait(domain, provider2);
 
         provider2.initialize(null);
 
-        assertThat(OpenFeatureAPI.getInstance().getClient(domain).getProviderState())
-                .isEqualTo(ProviderState.READY);
+        assertThat(api.getClient(domain).getProviderState()).isEqualTo(ProviderState.READY);
     }
 
     @Test
     void featureProviderTrackIsCalled() throws Exception {
         FeatureProvider featureProvider = mock(FeatureProvider.class);
-        FeatureProviderTestUtils.setFeatureProvider(featureProvider);
+        api.setProviderAndWait(featureProvider);
 
-        OpenFeatureAPI.getInstance()
-                .getClient()
-                .track("track-event", new ImmutableContext(), new MutableTrackingEventDetails(22.2f));
+        api.getClient().track("track-event", new ImmutableContext(), new MutableTrackingEventDetails(22.2f));
 
         verify(featureProvider).initialize(any());
         verify(featureProvider).getMetadata();
diff --git a/src/test/java/dev/openfeature/sdk/OpenFeatureAPITestUtil.java b/src/test/java/dev/openfeature/sdk/OpenFeatureAPITestUtil.java
new file mode 100644
index 0000000..f33c5b4
--- /dev/null
+++ b/src/test/java/dev/openfeature/sdk/OpenFeatureAPITestUtil.java
@@ -0,0 +1,10 @@
+package dev.openfeature.sdk;
+
+public class OpenFeatureAPITestUtil {
+
+    private OpenFeatureAPITestUtil() {}
+
+    public static OpenFeatureAPI createAPI() {
+        return new OpenFeatureAPI();
+    }
+}
diff --git a/src/test/java/dev/openfeature/sdk/OpenFeatureClientTest.java b/src/test/java/dev/openfeature/sdk/OpenFeatureClientTest.java
index 4f4d320..97a1417 100644
--- a/src/test/java/dev/openfeature/sdk/OpenFeatureClientTest.java
+++ b/src/test/java/dev/openfeature/sdk/OpenFeatureClientTest.java
@@ -38,7 +38,7 @@ class OpenFeatureClientTest implements HookFixtures {
     @Test
     @DisplayName("should not throw exception if hook has different type argument than hookContext")
     void shouldNotThrowExceptionIfHookHasDifferentTypeArgumentThanHookContext() {
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
+        OpenFeatureAPI api = new OpenFeatureAPI();
         api.setProviderAndWait(
                 "shouldNotThrowExceptionIfHookHasDifferentTypeArgumentThanHookContext", new DoSomethingProvider());
         Client client = api.getClient("shouldNotThrowExceptionIfHookHasDifferentTypeArgumentThanHookContext");
@@ -82,7 +82,7 @@ class OpenFeatureClientTest implements HookFixtures {
     @DisplayName("Should not call evaluation methods when the provider has state FATAL")
     void shouldNotCallEvaluationMethodsWhenProviderIsInFatalErrorState() {
         FeatureProvider provider = new TestEventsProvider(100, true, "fake fatal", true);
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
+        OpenFeatureAPI api = new OpenFeatureAPI();
         Client client = api.getClient("shouldNotCallEvaluationMethodsWhenProviderIsInFatalErrorState");
 
         assertThrows(
@@ -97,7 +97,7 @@ class OpenFeatureClientTest implements HookFixtures {
     @DisplayName("Should not call evaluation methods when the provider has state NOT_READY")
     void shouldNotCallEvaluationMethodsWhenProviderIsInNotReadyState() {
         FeatureProvider provider = new TestEventsProvider(5000);
-        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
+        OpenFeatureAPI api = new OpenFeatureAPI();
         api.setProvider("shouldNotCallEvaluationMethodsWhenProviderIsInNotReadyState", provider);
         Client client = api.getClient("shouldNotCallEvaluationMethodsWhenProviderIsInNotReadyState");
         FlagEvaluationDetails<Boolean> details = client.getBooleanDetails("key", true);
diff --git a/src/test/java/dev/openfeature/sdk/ProviderRepositoryTest.java b/src/test/java/dev/openfeature/sdk/ProviderRepositoryTest.java
index 9865263..7041df5 100644
--- a/src/test/java/dev/openfeature/sdk/ProviderRepositoryTest.java
+++ b/src/test/java/dev/openfeature/sdk/ProviderRepositoryTest.java
@@ -35,7 +35,7 @@ class ProviderRepositoryTest {
 
     @BeforeEach
     void setupTest() {
-        providerRepository = new ProviderRepository();
+        providerRepository = new ProviderRepository(new OpenFeatureAPI());
     }
 
     @Nested
diff --git a/src/test/java/dev/openfeature/sdk/ShutdownBehaviorSpecTest.java b/src/test/java/dev/openfeature/sdk/ShutdownBehaviorSpecTest.java
index e7caf92..1bb7d4b 100644
--- a/src/test/java/dev/openfeature/sdk/ShutdownBehaviorSpecTest.java
+++ b/src/test/java/dev/openfeature/sdk/ShutdownBehaviorSpecTest.java
@@ -1,6 +1,5 @@
 package dev.openfeature.sdk;
 
-import static dev.openfeature.sdk.testutils.FeatureProviderTestUtils.setFeatureProvider;
 import static org.mockito.Mockito.*;
 
 import dev.openfeature.sdk.fixtures.ProviderFixture;
@@ -15,9 +14,19 @@ import org.junit.jupiter.api.Test;
 class ShutdownBehaviorSpecTest {
 
     private String DOMAIN = "myDomain";
+    private OpenFeatureAPI api;
+
+    void setFeatureProvider(FeatureProvider featureProvider) {
+        api.setProviderAndWait(featureProvider);
+    }
+
+    void setFeatureProvider(String domain, FeatureProvider featureProvider) {
+        api.setProviderAndWait(domain, featureProvider);
+    }
 
     @BeforeEach
     void resetFeatureProvider() {
+        api = new OpenFeatureAPI();
         setFeatureProvider(new NoOpProvider());
     }
 
@@ -110,7 +119,6 @@ class ShutdownBehaviorSpecTest {
             FeatureProvider namedProvider = ProviderFixture.createMockedProvider();
             setFeatureProvider(defaultProvider);
             setFeatureProvider(DOMAIN, namedProvider);
-            OpenFeatureAPI api = OpenFeatureAPI.getInstance();
 
             synchronized (OpenFeatureAPI.class) {
                 api.shutdown();
@@ -125,15 +133,14 @@ class ShutdownBehaviorSpecTest {
         @Test
         @DisplayName("once shutdown is complete, api must be ready to use again")
         void apiIsReadyToUseAfterShutdown() {
-            final OpenFeatureAPI openFeatureAPI = OpenFeatureAPI.getInstance();
 
             NoOpProvider p1 = new NoOpProvider();
-            openFeatureAPI.setProvider(p1);
+            api.setProvider(p1);
 
-            openFeatureAPI.shutdown();
+            api.shutdown();
 
             NoOpProvider p2 = new NoOpProvider();
-            openFeatureAPI.setProvider(p2);
+            api.setProvider(p2);
         }
     }
 }
diff --git a/src/test/java/dev/openfeature/sdk/TrackingSpecTest.java b/src/test/java/dev/openfeature/sdk/TrackingSpecTest.java
index a8f6e30..ba35437 100644
--- a/src/test/java/dev/openfeature/sdk/TrackingSpecTest.java
+++ b/src/test/java/dev/openfeature/sdk/TrackingSpecTest.java
@@ -15,7 +15,6 @@ import static org.mockito.Mockito.verify;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import dev.openfeature.sdk.fixtures.ProviderFixture;
-import dev.openfeature.sdk.testutils.FeatureProviderTestUtils;
 import java.util.HashMap;
 import java.util.Map;
 import lombok.SneakyThrows;
@@ -29,7 +28,7 @@ class TrackingSpecTest {
 
     @BeforeEach
     void getApiInstance() {
-        api = OpenFeatureAPI.getInstance();
+        api = new OpenFeatureAPI();
         client = api.getClient();
     }
 
@@ -116,7 +115,7 @@ class TrackingSpecTest {
         client.setEvaluationContext(clCtx);
 
         FeatureProvider provider = ProviderFixture.createMockedProvider();
-        FeatureProviderTestUtils.setFeatureProvider(provider);
+        api.setProviderAndWait(provider);
 
         client.track("event", new MutableContext().add("my-key", "final"), new MutableTrackingEventDetails(0.0f));
 
@@ -170,8 +169,7 @@ class TrackingSpecTest {
                 .add("my-struct", new Value(new MutableTrackingEventDetails()));
 
         assertEquals(expectedMap, details.asMap());
-        assertThatCode(() -> OpenFeatureAPI.getInstance()
-                        .getClient()
+        assertThatCode(() -> api.getClient()
                         .track("tracking-event-name", new ImmutableContext(), new MutableTrackingEventDetails()))
                 .doesNotThrowAnyException();
 
@@ -188,8 +186,7 @@ class TrackingSpecTest {
 
         ImmutableTrackingEventDetails immutableDetails = new ImmutableTrackingEventDetails(2, expectedMap);
         assertEquals(expectedImmutable, immutableDetails.asMap());
-        assertThatCode(() -> OpenFeatureAPI.getInstance()
-                        .getClient()
+        assertThatCode(() -> api.getClient()
                         .track("tracking-event-name", new ImmutableContext(), new ImmutableTrackingEventDetails()))
                 .doesNotThrowAnyException();
     }
diff --git a/src/test/java/dev/openfeature/sdk/arch/ArchitectureTest.java b/src/test/java/dev/openfeature/sdk/arch/ArchitectureTest.java
new file mode 100644
index 0000000..8bf8b28
--- /dev/null
+++ b/src/test/java/dev/openfeature/sdk/arch/ArchitectureTest.java
@@ -0,0 +1,27 @@
+package dev.openfeature.sdk.arch;
+
+import static com.tngtech.archunit.base.DescribedPredicate.describe;
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
+
+import com.tngtech.archunit.junit.AnalyzeClasses;
+import com.tngtech.archunit.junit.ArchTest;
+import com.tngtech.archunit.lang.ArchRule;
+
+@AnalyzeClasses(packages = "dev.openfeature.sdk")
+public class ArchitectureTest {
+
+    @ArchTest
+    public static final ArchRule avoidGetInstances = noClasses()
+            .that()
+            .resideOutsideOfPackages("..benchmark", "..e2e.*")
+            .and()
+            .haveSimpleNameNotEndingWith("SingeltonTest")
+            .should()
+            .callMethodWhere(describe(
+                    "Avoid Internal usage of OpenFeatureAPI.GetInstances",
+                    // Target method may not reside in class annotated with BusinessException
+                    methodCall ->
+                            methodCall.getTarget().getOwner().getFullName().equals("dev.openfeature.sdk.OpenFeatureAPI")
+                                    // And target method may not have the static modifier
+                                    && methodCall.getTarget().getName().equals("getInstance")));
+}
diff --git a/src/test/java/dev/openfeature/sdk/providers/memory/InMemoryProviderTest.java b/src/test/java/dev/openfeature/sdk/providers/memory/InMemoryProviderTest.java
index 86782b3..4d2a8b2 100644
--- a/src/test/java/dev/openfeature/sdk/providers/memory/InMemoryProviderTest.java
+++ b/src/test/java/dev/openfeature/sdk/providers/memory/InMemoryProviderTest.java
@@ -12,6 +12,7 @@ import dev.openfeature.sdk.Client;
 import dev.openfeature.sdk.EventDetails;
 import dev.openfeature.sdk.ImmutableContext;
 import dev.openfeature.sdk.OpenFeatureAPI;
+import dev.openfeature.sdk.OpenFeatureAPITestUtil;
 import dev.openfeature.sdk.Value;
 import dev.openfeature.sdk.exceptions.FlagNotFoundError;
 import dev.openfeature.sdk.exceptions.ProviderNotReadyError;
@@ -25,18 +26,20 @@ import org.junit.jupiter.api.Test;
 
 class InMemoryProviderTest {
 
-    private static Client client;
+    private Client client;
 
-    private static InMemoryProvider provider;
+    private InMemoryProvider provider;
+    private OpenFeatureAPI api;
 
     @SneakyThrows
     @BeforeEach
     void beforeEach() {
         Map<String, Flag<?>> flags = buildFlags();
         provider = spy(new InMemoryProvider(flags));
-        OpenFeatureAPI.getInstance().onProviderConfigurationChanged(eventDetails -> {});
-        OpenFeatureAPI.getInstance().setProviderAndWait(provider);
-        client = OpenFeatureAPI.getInstance().getClient();
+        api = OpenFeatureAPITestUtil.createAPI();
+        api.onProviderConfigurationChanged(eventDetails -> {});
+        api.setProviderAndWait(provider);
+        client = api.getClient();
         provider.updateFlags(flags);
         provider.updateFlag(
                 "addedFlag",
@@ -107,8 +110,8 @@ class InMemoryProviderTest {
         Consumer<EventDetails> handler = mock(Consumer.class);
         Map<String, Flag<?>> flags = buildFlags();
 
-        OpenFeatureAPI.getInstance().onProviderConfigurationChanged(handler);
-        OpenFeatureAPI.getInstance().setProviderAndWait(provider);
+        api.onProviderConfigurationChanged(handler);
+        api.setProviderAndWait(provider);
 
         provider.updateFlags(flags);
 
diff --git a/src/test/java/dev/openfeature/sdk/testutils/FeatureProviderTestUtils.java b/src/test/java/dev/openfeature/sdk/testutils/FeatureProviderTestUtils.java
deleted file mode 100644
index c9ad77d..0000000
--- a/src/test/java/dev/openfeature/sdk/testutils/FeatureProviderTestUtils.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package dev.openfeature.sdk.testutils;
-
-import static org.awaitility.Awaitility.await;
-
-import dev.openfeature.sdk.FeatureProvider;
-import dev.openfeature.sdk.OpenFeatureAPI;
-import java.time.Duration;
-import java.util.function.Function;
-import lombok.experimental.UtilityClass;
-
-// todo check the need of this utility class as we now have setProviderAndWait capability
-@UtilityClass
-public class FeatureProviderTestUtils {
-
-    public static void setFeatureProvider(FeatureProvider provider) {
-        OpenFeatureAPI.getInstance().setProvider(provider);
-        waitForProviderInitializationComplete(OpenFeatureAPI::getProvider, provider);
-    }
-
-    private static void waitForProviderInitializationComplete(
-            Function<OpenFeatureAPI, FeatureProvider> extractor, FeatureProvider provider) {
-        await().pollDelay(Duration.ofMillis(1))
-                .atMost(Duration.ofSeconds(1))
-                .until(() -> extractor.apply(OpenFeatureAPI.getInstance()).equals(provider));
-    }
-
-    public static void setFeatureProvider(String domain, FeatureProvider provider) {
-        OpenFeatureAPI.getInstance().setProvider(domain, provider);
-        waitForProviderInitializationComplete(api -> api.getProvider(domain), provider);
-    }
-}