1 Formål

Formålet med dette dokument er at beskrive hvordan et udviklingsmiljø, til videreudvikling af SEAL.JAVA services, kan sættes op, samt hvordan koden bygges, deployes og testes.
Først beskrives de softwaremæssige krav, der er til miljøet, samt hvordan kode hentes og bygges. Dernæst beskrives deploymentmiljøet.

2 Opsætning

Siden release 2.5.0 har Seal.java krævet Java 1.8  for at bygge

2.1 Kildekode

Kildekoden til Seal.java er efter version 2.5.11 flyttet til libraries repository  på NSPOPs subversion.

https://svn.nspop.dk/svn/libraries/sosi-seal/

Her vil fremtidige udgaver af koden blive versioneret. Udgaverne for version 2.5.9, 2.5.10 og 2.5.11 er også blevet flyttet til libraries  og kan findes her:

https://svn.nspop.dk/svn/libraries/sosi-seal/tags/

2.2 Distribution

De binære pakker er tilgængelige igennem NSPOPs Nexus (pakke manager):

https://nexus.nspop.dk/nexus/service/rest/repository/browse/public/dk/sosi/seal/seal/

Her findes også SNAPSHOT udgaver, som kan bruges under udvikling af funktionalitet, der skal testes inden release.

2.3 Bygge og unittest

Koden bygges vha. Maven.

Følgende bruges til at bygge Seal.java:

mvn -f modules/pom.xml package install

Unitests afvikles under byg, men kan også kaldes selvstændigt vha.:

mvn -f modules/pom.xml test


3 Udvikling

Herunder vejledning til udviklingen i forskellige områder af Seal.java

3.1 parametriserede tests

Vi bruger JUnit 4 til at teste i Seal.java og vil gerne kunne afvikle parameteriserede tests mod de funktioner, hvor afviklingen er uændret, men input er variabelt. Det har primært været et ønske ifm. introduktionen af nye OCES3 certifikater, hvor snitfladerne har været uændret, men input har været henholdsvis OCES2 og OCES3 certifikater.

Følgende side har været indgangen til brug af parametriserede tests:

https://github.com/junit-team/junit4/wiki/Parameterized-tests

I dette afsnit følger en kort introduktion til den skabelon man skal følge for at lave en standard parametriseret JUnit4 test - efterfulgt af et eksempel.

Så følger en kort beskrivelse af de problemstillinger vi har haft ifm. oprettelsen af tests. Vi gennemgår de hjælpeklasser og beslutninger, som er lavet for at give et bedre overblik og en nemmere tilgang til at lave parametriseret tests - og til sidst et eksempel på brugen af hjælpeklasserne.

3.1.1 Introduktion til Parameterized tests

En parametriseret JUnit4 test klasse skal afvikles med org.junit.runners.Parameterized. Derfor angives annotation @RunWith(Parameterized.class) på klassen.

Der skal angives en metode, som annoteres med org.junit.runners.Parameterized.Parameters, som er de parametre der bruges i test metoderne. Metoden returnere en java.lang.Iterable bestående af et Object[], hvor hvert Object[] er det datasæt af parametre, som et test genneløb skal bruge. Testene gennemløbes for hvert entry der er i java.lang.Iterable.

Inputtet 'name' til org.junit.runners.Parameterized.Parameters annoteringen angiver navnet på parametriseret test.

Datasæt til tests angives vha. org.junit.runners.Parameterized.Parameter. Her angiver man et index som input til annoteringen, så det peger på et specifikt Object i aktuelle Object[].

Eksempel:

Kompleks Parametriseret Unittest
@RunWith(Parameterized.class)
public class SimpleVaultTest {
	@Parameter(0)
	public X509Certificate certificateToTest; //Skal være public!
	@Parameter(1)
	public String expectedCommonName; 
	@Parameter(2)
	public String expectedSubjectSerialNumber; 
	@Parameters
	public static Iterable<Object[]> data() {
		return Arrays.asList(new Object[][] {{ CredentialVaultTestUtil.getCredentialVault().getSystemCredentialPair().getCertificate(), 
					"TU GENEREL MOCES M CPR gyldig",
					"CVR:30808460-RID:42634739"
				}, { CredentialVaultTestUtil.getMoces3CredentialVault().getSystemCredentialPair().getCertificate(), 
					"Tonnis Vestergaard",
					"UI:DK-153279bd-07cb-4128-a8b1-666107fba6d5"}});
	}
	@Test
	public void testCertificateCommonName() {
		DistinguishedName dn = new DistinguishedName(certificateToTest.getSubjectX500Principal());
		assertEquals("Forventede noget helt andet her?", expectedCommonName, dn.getCommonName());
	}
	@Test
	public void testCertificateSubjectSerialNumber() {
		DistinguishedName dn = new DistinguishedName(certificateToTest.getSubjectX500Principal());
		assertEquals("Forventede noget helt andet her?", expectedSubjectSerialNumber, dn.getSubjectSerialNumber());
	}
}

3.1.2 Hjælpeklasser til Parameterized tests i Seal.java

I forbindelse med implemeneteringen af parametriserede tests stødte vi nogle mindre udfordringer.

  • Navnet på den parametriserede test var det indeks testen havde. Så den første test hed f.eks. [0] og selve testen ville hedde testCertificateCommonName[0].
  • Vi ønskede kun at det var input parametre der blev angivet som parameter i selve testen. Vi ønskede derfor ikke en angivelse af paramtre ala expectedResult1, expectedException2.....

Derfor er der implementeret følgende:

  • @Parameter(0) - Bruges altid til navnet på den parametriserede test. Denne værdi kan bruges i org.junit.runners.Parameterized.Parameters ved at angive name="{0}"
  • @Parameter(1) - Bruges til at holde Validators, som indeholder 0-* valideringer der kan bruges i tests.
  • @Parameter(2):@Parameter(n) - Bruges til at angive de parametre der er input til tests.

Derfor er findes følgende hjælpeklasser i Seal.java:

  • TestParameterBuilder - Klasse til at oprette de parametriserede tests og tilhørende data. Ved kald af build returneres en liste af arrays.
  • TestParameter - For hver parametriseret test findes dette Objekt. Indholder navn, parametre og evt. valideringer.
  • Validators - En samling af valideringer (Validation) som hentes og gemmes med vha. unik tekst.
  • Validation - En klasse til at udføre valideringen. En validation hentes fra Validators og udstiller metoderne:
    • assertSucces() og assertSuccess(resultat): Hvis testen er gennemfært uden fejl
    • assertFailure() og assertFailure(exception): Hvis testen er gennemført med fejl.
  • Asserter - Et interface til at implementere egen validering.

Eksempel:

Kompleks unittest med TestParamterBuilder
@RunWith(Parameterized.class)
public class SimpleVaultTest {
	@Parameter(0)
	public String suiteName;
	//Then
	@Parameter(1)
	public Validators validators;
	//Given
	@Parameter(2)
	public X509Certificate certificateToTest;
	
	public Date validityDate;

	@Parameters(name = "{0}")
	public static Iterable<Object[]> data() {
		TestParameterBuilder builder = new TestParameterBuilder();
		
		TestParameter credentialVaultTest = builder.newTest("CredentialVault");
		//Given
		credentialVaultTest.add(CredentialVaultTestUtil.getCredentialVault().getSystemCredentialPair().getCertificate());
		//Then
		credentialVaultTest.expect("CertificateCommonName", "TU GENEREL MOCES M CPR gyldig");
		credentialVaultTest.expect("SubjectSerialNumber", "CVR:30808460-RID:42634739");
		credentialVaultTest.success("CertificateValidity");
		credentialVaultTest.success("Custom", notNorwayAssertion());

		TestParameter moces3CredentialVaultTest = builder.newTest("Moces3CredentialVault");
		//Given
		moces3CredentialVaultTest.add(CredentialVaultTestUtil.getMoces3CredentialVault().getSystemCredentialPair().getCertificate());
		//Then
		moces3CredentialVaultTest.expect("CertificateCommonName", "Tonnis Vestergaard");
		moces3CredentialVaultTest.expect("SubjectSerialNumber", "UI:DK-153279bd-07cb-4128-a8b1-666107fba6d5");
		moces3CredentialVaultTest.fail("CertificateValidity", new CertificateNotYetValidException("NotBefore: Tue Jan 26 15:27:19 CET 2021"));
		moces3CredentialVaultTest.success("Custom", notNorwayAssertion());
		
		return builder.build();
	}
	//Custom Asserter	
	private static Asserter notNorwayAssertion() {
		return (actual) -> {
			Assert.assertNotNull("Forventede et reultat", actual);
			Assert.assertTrue("Forventer instans af DistinguishedName", actual instanceof DistinguishedName);
			DistinguishedName dn = (DistinguishedName) actual;
			Assert.assertNotEquals("Forventede ikke Norge",  "N", dn.getCountry());
		};
	}
	@Before
	public void setup() {
		validityDate = Date.from(Instant.parse("2020-01-01T00:00:00.00Z"));
	}
	//When
	@Test
	public void testCertificateCommonName() {
		DistinguishedName dn = new DistinguishedName(certificateToTest.getSubjectX500Principal());		
		validators.getValidator("CertificateCommonName").assertSuccess(dn.getCommonName());
	}
	@Test
	public void testCertificateSubjectSerialNumber() {
		DistinguishedName dn = new DistinguishedName(certificateToTest.getSubjectX500Principal());
		validators.getValidator("SubjectSerialNumber").assertSuccess(dn.getSubjectSerialNumber());
	}
	@Test
	public void testCertificateValidity() throws CertificateExpiredException, CertificateNotYetValidException {
		Validation validator = validators.getValidator("CertificateValidity");
		try {
			certificateToTest.checkValidity(validityDate);
			validator.assertSuccess();
		} catch(Exception e) {
			validator.assertFailure(e);
		}
	}
	@Test
	public void testCustomFieldValidation() {
		DistinguishedName dn = new DistinguishedName(certificateToTest.getSubjectX500Principal());		
		validators.getValidator("Custom").assertSuccess(dn);
	}
}

Output af ovenstående eksempel vil være:

















  • No labels