Testing ALFA policies
Testing is essential to verify that an ALFA project functions correctly and meets its requirements. Axiomatics Policy DevOps supports various testing stages, validating your domains, policies, and attribute connectors for production use. Specifically:
Unit testing (Dev pipeline | pre-deployment)
- Policy: Test a specific policy or rule in isolation for a given use case, without attribute connectors or other policies.
- Attribute connector: Test that a specific attribute connector can connect to an external PIP and retrieve attributes.
Integration testing (Dev pipeline | pre-deployment)
- Single policy: Test the successful integration of a specific policy for a given use case with its required attribute connectors.
- Multiple policies: Test the combined functionality of multiple use cases with multiple policies, including the correct operation of combination algorithms (conflict resolution).
System/acceptance testing (Post-deployment | product environment may be excluded)
- Test that the main policy addresses all functional and business use cases.
Policy JUnit tests
Follow the steps below to write JUnit tests for ALFA policies. These tests simulate authorization requests to verify policy decisions, attribute handling, and access control behavior.
Create a Java test class under
src/test./src/test/MyUnitTest.javaimport org.junit.jupiter.api.extension.RegisterExtension;
import com.axiomatics.cr.alfa.test.junit.AlfaExtension;
public class MyUnitTest {
@RegisterExtension
public AlfaExtension alfa = new AlfaExtension();Specify the policy under test. This can be the main policy or a specific policy, depending on whether it's a system, integration, or unit test.
@RegisterExtension
public AlfaExtension target = new AlfaExtension().withMainPolicy("acme.Main");notewithMainPolicyis not needed if youralfaSpecificationsdirectory contains only one root policy.Execute a test and use one of the following Hamcrest matchers for assertions:
permit(),deny(),notApplicable(), orintermediate().import static com.axiomatics.cr.alfa.test.junit.matchers.AlfaMatchers.*
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
...
@Test
public void shouldNotPermitConsultantsToApprove() {
TestResponse result = target.newTestRequest()
.with("user.role", "consultant")
.with("action", "approve")
.evaluate();
assertThat(result, is(not(permit())));noteUse Hamcrest predicates such as
is()andis(not()). See the Hamcrest TutorialOpens in a new tab for more information.ImportantTest method names should be descriptive and sufficient to implement and verify the test. If you have a test case specification, you can reference it. Don't be concerned if the name is lengthy.
Test obligations or advice in your policy as in the examples below.
tipObligations, typically associated with permit decisions, are mandatory actions that must be performed when a condition or decision is met. Advice is optional info commonly associated with deny decisions, offering explanations or guidance regarding the denied access.
Example 1:
Advice assertion exampleimport static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.MatcherAssert.assertThat;
import static com.axiomatics.cr.alfa.test.junit.matchers.AttributeAssignmentMatcher.withIdAndText;
import static com.axiomatics.cr.alfa.test.junit.matchers.AttributeAssignmentMatcher.withText;
TestResponse result = target.evaluate();
assertThat(result.getAdvice(), hasItem(withText("Advice from PDP")));Example 2:
Obligation assertion example targeting specific attributeimport static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.MatcherAssert.assertThat;
import static com.axiomatics.cr.alfa.test.junit.matchers.AttributeAssignmentMatcher.withIdAndText;
import static com.axiomatics.cr.alfa.test.junit.matchers.AttributeAssignmentMatcher.withText;
TestResponse result = target.evaluate();
assertThat(result.getObligation(), hasItem(withIdAndText("org.obligation.message", "Obligation from PDP")));ImportantFor unit tests, avoid
withAttributeConnectors(). Simulate key attributes using.with(...)for test input.
Attribute connectors tests
You should also run unit tests for your attribute connectors. Create a test file with the content below.
import com.axiomatics.cr.alfa.test.junit.AlfaExtension;
import com.axiomatics.cr.alfa.test.junit.AttributeConnector;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.MatcherAssert.assertThat;
public class OurConnectorUnitTest {
@RegisterExtension
public AlfaExtension alfa = new AlfaExtension();
@Test
public void shouldGetRoleConsultantforIdentityCecilia() {
AttributeConnector target = alfa.newAttributeTest("ourConnector");
List<String> result = target.lookup("user.role").by("user.identity", "cecilia");
assertThat(result, hasItem("consultant"));
}
| Method | Description |
|---|---|
newAttributeTest(...) | Enter the filename of your attribute connector (without the extension) located in src/authorizationDomain/attributeConnectors. |
lookup(...) | Specify the attribute (as a string) that you want to retrieve from your PIP. |
by(...) | Provide pairs of strings representing the key attribute names and values required for the lookup. |
For example, if an attribute connector retrieves a user's approval limit based on their identity and the purchase order's department, the lookup might look like this:
target.lookup("user.approval.limit").by("user.identity", "cecilia", "resource.purchaseOrder.department", "InternalSales");
For unit tests, add any environment variables required by attribute connectors, such as URLs and credentials, to the test section of your build.gradle file.
Chained attribute connectors
When using chained attribute connectors that rely on each other, you can create an integration test by feeding the output of one connector as input to another. This is particularly useful when combining HTTP and Parser attribute connectors.
@Test
public void shouldGetCleareanceForUserAlice() {
String user = "alice";
String userServiceHttpBodyValue = rule.newAttributeTest("userHttpService").lookup("user.serviceHttpBody").by("user.identity", user).get(0);
String userClearance = rule.newAttributeTest("userHttpServiceParser").lookup("user.clearance").by("user.serviceHttpBody",userServiceHttpBodyValue).get(0);
assertThat(userClearance, is("classified"));
}
Authorization domain system tests
Authorization domain system tests evaluate all your policies and attribute connectors together to ensure they satisfy the use cases and requirements.
package com.myorg.alfa;
import com.axiomatics.cr.alfa.test.junit.AlfaExtension;
import com.axiomatics.cr.alfa.test.junit.TestRequest;
import com.axiomatics.cr.alfa.test.junit.TestResponse;
import org.junit.jupiter.api.Test;
import ort org.junit.jupiter.api.extension.RegisterExtension;
import static com.axiomatics.cr.alfa.test.junit.matchers.AlfaMatchers.permit;
import static com.axiomatics.cr.alfa.test.junit.matchers.AttributeAssignmentMatcher.withText;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
public class MySystemTest {
@RegisterExtension
public AlfaExtension alfa = new AlfaExtension().withAttributeConnectors();
@Test
public void shouldGiveMartinAccessToResource1() {
TestRequest target = alfa.newTestRequest()
.with("user.identity", "martin")
.with("resource.identity", "1");
TestResponse result = target.evaluate();
assertThat(result, is(permit()));
assertThat(result.getAdvice(), hasItem(withText("Permit since user is manager")));
}
}
To create a system test:
- Omit the
withMainPolicy()declaration in the test rule. This defaults to testing the main policy for your domain (specified inbuild.gradle). - Add the
withAttributeConnectors()declaration to include all attribute connectors in the test.
The required environment variables should already be defined from the attribute connector unit tests.