Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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.

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:

Code Block
languagejava
titleKompleks 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());
	}
}

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:

Code Block
languagejava
titleKompleks 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:
Image Added