diff --git a/doc/sphinx-guides/source/developers/testing.rst b/doc/sphinx-guides/source/developers/testing.rst index ac43d4111ff..e8d8af70940 100755 --- a/doc/sphinx-guides/source/developers/testing.rst +++ b/doc/sphinx-guides/source/developers/testing.rst @@ -60,6 +60,10 @@ Refactoring Code to Make It Unit-Testable Existing code is not necessarily written in a way that lends itself to easy testing. Generally speaking, it is difficult to write unit tests for both JSF "backing" beans (which end in ``Page.java``) and "service" beans (which end in ``Service.java``) because they require the database to be running in order to test them. If service beans can be exercised via API they can be tested with integration tests (described below) but a good technique for making the logic testable it to move code to "util beans" (which end in ``Util.java``) that operate on Plain Old Java Objects (POJOs). ``PrivateUrlUtil.java`` is a good example of moving logic from ``PrivateUrlServiceBean.java`` to a "util" bean to make the code testable. +Parameterized Tests and JUnit Theories +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Often times you will want to test a method multiple times with similar values. In order to avoid test bloat (writing a test for every data combination), JUnit offers Data-driven unit tests with ``Parameterized.class`` and ``Theories.class``. This allows a test to be run for each set of defined data values. For reference, take a look at issue https://github.com/IQSS/dataverse/issues/5619 . + Observing Changes to Code Coverage ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/test/java/edu/harvard/iq/dataverse/confirmemail/ConfirmEmailUtilTest.java b/src/test/java/edu/harvard/iq/dataverse/confirmemail/ConfirmEmailUtilTest.java index 78d64130e86..8fdc7dc38d5 100644 --- a/src/test/java/edu/harvard/iq/dataverse/confirmemail/ConfirmEmailUtilTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/confirmemail/ConfirmEmailUtilTest.java @@ -1,40 +1,61 @@ package edu.harvard.iq.dataverse.confirmemail; import java.sql.Timestamp; +import java.util.Collection; +import java.util.Arrays; + import static org.junit.Assert.assertEquals; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.experimental.runners.Enclosed; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +@RunWith(Enclosed.class) public class ConfirmEmailUtilTest { - @Test - public void testFriendlyExpirationTime() { - System.out.println("Friendly expiration timestamp / measurement test"); - System.out.println("1440 Minutes: " + ConfirmEmailUtil.friendlyExpirationTime(1440)); - assertEquals("24 hours", ConfirmEmailUtil.friendlyExpirationTime(1440)); - System.out.println("60 Minutes: " + ConfirmEmailUtil.friendlyExpirationTime(60)); - assertEquals("1 hour", ConfirmEmailUtil.friendlyExpirationTime(60)); - System.out.println("30 Minutes: " + ConfirmEmailUtil.friendlyExpirationTime(30)); - assertEquals("30 minutes", ConfirmEmailUtil.friendlyExpirationTime(30)); - System.out.println("90 Minutes: " + ConfirmEmailUtil.friendlyExpirationTime(90)); - assertEquals("1.5 hours", ConfirmEmailUtil.friendlyExpirationTime(90)); - System.out.println("2880 minutes: " + ConfirmEmailUtil.friendlyExpirationTime(2880)); - assertEquals("48 hours", ConfirmEmailUtil.friendlyExpirationTime(2880)); - System.out.println("150 minutes: " + ConfirmEmailUtil.friendlyExpirationTime(150)); - assertEquals("2.5 hours", ConfirmEmailUtil.friendlyExpirationTime(150)); - System.out.println("165 minutes: " + ConfirmEmailUtil.friendlyExpirationTime(165)); - assertEquals("2.75 hours", ConfirmEmailUtil.friendlyExpirationTime(165)); - System.out.println("1 Minute: " + ConfirmEmailUtil.friendlyExpirationTime(1)); - assertEquals("1 minute", ConfirmEmailUtil.friendlyExpirationTime(1)); - System.out.println(); - } + @RunWith(Parameterized.class) + public static class ConfirmEmailUtilParamTest { + + public String timeAsFriendlyString; + public int timeInMinutes; + + public ConfirmEmailUtilParamTest(String timeAsFriendlyString, int timeInSeconds) { + this.timeAsFriendlyString = timeAsFriendlyString; + this.timeInMinutes = timeInSeconds; + } - @Test - public void testGrandfatheredTime() { - System.out.println(); - System.out.println("Grandfathered account timestamp test"); - System.out.println("Grandfathered Time (y2k): " + ConfirmEmailUtil.getGrandfatheredTime()); - assertEquals(Timestamp.valueOf("2000-01-01 00:00:00.0"), ConfirmEmailUtil.getGrandfatheredTime()); - System.out.println(); + @Parameters + public static Collection parameters() { + return Arrays.asList( + new Object[][] { + { "48 hours", 2880 }, + { "24 hours", 1440 }, + { "2.75 hours", 165 }, + { "2.5 hours", 150 }, + { "1.5 hours", 90 }, + { "1 hour", 60 }, + { "30 minutes", 30 }, + { "1 minute", 1 } + } + ); + } + + @Test + public void friendlyExpirationTimeTest() { + assertEquals(timeAsFriendlyString, ConfirmEmailUtil.friendlyExpirationTime(timeInMinutes)); + } } + public static class ConfirmEmailUtilNoParamTest { + + @Test + public void testGrandfatheredTime() { + System.out.println(); + System.out.println("Grandfathered account timestamp test"); + System.out.println("Grandfathered Time (y2k): " + ConfirmEmailUtil.getGrandfatheredTime()); + assertEquals(Timestamp.valueOf("2000-01-01 00:00:00.0"), ConfirmEmailUtil.getGrandfatheredTime()); + System.out.println(); + } + } } diff --git a/src/test/java/edu/harvard/iq/dataverse/util/JsfHelperTest.java b/src/test/java/edu/harvard/iq/dataverse/util/JsfHelperTest.java index 9efdf652d94..545d3b1a31f 100644 --- a/src/test/java/edu/harvard/iq/dataverse/util/JsfHelperTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/util/JsfHelperTest.java @@ -4,24 +4,29 @@ package edu.harvard.iq.dataverse.util; +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.Collection; + import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; /** * * @author michael */ +@RunWith(Parameterized.class) public class JsfHelperTest { enum TestEnum { Lorem, Ipsum, Dolor, Sit, Amet } - public JsfHelperTest() { - } - @BeforeClass public static void setUpClass() { } @@ -38,6 +43,27 @@ public void setUp() { public void tearDown() { } + public TestEnum inputEnum; + public String inputString; + public TestEnum defaultEnumValue; + + public JsfHelperTest(TestEnum inputEnum, String inputString, TestEnum defaultEnumValue) { + this.inputEnum = inputEnum; + this.inputString = inputString; + this.defaultEnumValue = defaultEnumValue; + } + + @Parameters + public static Collection parameters() { + return Arrays.asList ( + new Object[][] { + { TestEnum.Lorem, "Lorem", TestEnum.Dolor }, + { TestEnum.Lorem, "Lorem ", TestEnum.Dolor }, + { TestEnum.Dolor, null, TestEnum.Dolor }, + { TestEnum.Dolor, "THIS IS A BAD VALUE", TestEnum.Dolor }, + } + ); + } /** * Test of enumValue method, of class JsfHelper. @@ -46,12 +72,8 @@ public void tearDown() { public void testEnumValue() { System.out.println("enumValue"); JsfHelper instance = new JsfHelper(); - - assertEquals( TestEnum.Lorem, instance.enumValue("Lorem", TestEnum.class, TestEnum.Dolor) ); - assertEquals( TestEnum.Lorem, instance.enumValue("Lorem ", TestEnum.class, TestEnum.Dolor) ); - assertEquals( TestEnum.Dolor, instance.enumValue(null, TestEnum.class, TestEnum.Dolor) ); - assertEquals( TestEnum.Dolor, instance.enumValue("THIS IS A BAD VALUE", TestEnum.class, TestEnum.Dolor) ); - + + assertEquals( inputEnum, instance.enumValue(inputString, TestEnum.class, defaultEnumValue) ); } } diff --git a/src/test/java/edu/harvard/iq/dataverse/util/StringUtilTest.java b/src/test/java/edu/harvard/iq/dataverse/util/StringUtilTest.java index b13aa694754..61c0ba6d54d 100644 --- a/src/test/java/edu/harvard/iq/dataverse/util/StringUtilTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/util/StringUtilTest.java @@ -1,6 +1,11 @@ package edu.harvard.iq.dataverse.util; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.stream.Stream; import org.junit.After; @@ -8,14 +13,19 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + /** * * @author michael */ +@RunWith(Enclosed.class) public class StringUtilTest { - + public StringUtilTest() { } @@ -35,107 +45,173 @@ public void setUp() { public void tearDown() { } - /** - * Test of isEmpty method, of class StringUtil. - */ - @Test - public void testIsEmpty() { - assertTrue( StringUtil.isEmpty(null) ); - assertTrue( StringUtil.isEmpty("") ); - assertTrue( StringUtil.isEmpty(" ") ); - assertTrue( StringUtil.isEmpty("\t") ); - assertTrue( StringUtil.isEmpty("\t \t \n") ); - assertFalse( StringUtil.isEmpty("a") ); - } + @RunWith(Parameterized.class) + public static class TestIsEmpty { - /** - * Test of isAlphaNumeric method, of class StringUtil. - */ - @Test - public void testIsAlphaNumeric() { - assertTrue( StringUtil.isAlphaNumeric("abc") ); - assertTrue( StringUtil.isAlphaNumeric("1230") ); - assertTrue( StringUtil.isAlphaNumeric("1230abc") ); - assertTrue( StringUtil.isAlphaNumeric("1230abcABC") ); - assertFalse( StringUtil.isAlphaNumeric("1230abcABC#") ); - } + public boolean isValid; + public String inputString; + + public TestIsEmpty(boolean isValid, String inputString) { + this.isValid = isValid; + this.inputString = inputString; + } - /** - * Test of isAlphaNumericChar method, of class StringUtil. - */ - @Test - public void testIsAlphaNumericChar() { - assertTrue( StringUtil.isAlphaNumericChar('a') ); - assertTrue( StringUtil.isAlphaNumericChar('f') ); - assertTrue( StringUtil.isAlphaNumericChar('z') ); - assertTrue( StringUtil.isAlphaNumericChar('0') ); - assertTrue( StringUtil.isAlphaNumericChar('1') ); - assertTrue( StringUtil.isAlphaNumericChar('9') ); - assertTrue( StringUtil.isAlphaNumericChar('A') ); - assertTrue( StringUtil.isAlphaNumericChar('G') ); - assertTrue( StringUtil.isAlphaNumericChar('Z') ); - assertFalse( StringUtil.isAlphaNumericChar('@') ); - } + @Parameters + public static Collection parameters() { + return Arrays.asList( + new Object[][] { + { true, null }, + { true, "" }, + { true, " " }, + { true, "\t" }, + { true, "\t \t \n" }, + { false, "a" }, + } + ); + } - @Test - public void testHtml2Text() { - assertEquals(StringUtil.html2text("be bold!"), "be bold!"); - assertEquals(StringUtil.html2text(null), null); - assertEquals(StringUtil.html2text("

Description:
\n" - + "Data were taken May-June 2003 and 2005. Flux units are in mJy per 31 arcsecond beam.\n" - + "

\n" - + "\n" - + "

Telescope Information
\n" - + "Caltech Submillimeter Observatory

\n" - + "\n" - + "

Status:
\n" - + "Final

\n" - + "\n" - + "

Sampling:
\n" - + "Sensitivity: Average 1 sigma rms = 10 mJy per beam.
\n" - + "Waveband: 1120 microns
\n" - + "Resolution: 31 arcsecond beam in 10 arcsecond pixels (diffuse large-scale structure is lost)\n" - + "

\n" - + "\n" - + "

Areal Coverage:
\n" - + "11 square degrees\n" - + "

\n" - + "\n" - + "

Map Center (Galactic):
\n" - + "NA
\n" - + "NA

\n" - + "\n" - + "

Map Center (J2000):
\n" - + "RA = 18:29:00
\n" - + "Dec = +00:30:00

"), "Description: Data were taken May-June 2003 and 2005. Flux units are in mJy per 31 arcsecond beam. Telescope Information Caltech Submillimeter Observatory Status: Final Sampling: Sensitivity: Average 1 sigma rms = 10 mJy per beam. Waveband: 1120 microns Resolution: 31 arcsecond beam in 10 arcsecond pixels (diffuse large-scale structure is lost) Areal Coverage: 11 square degrees Map Center (Galactic): NA NA Map Center (J2000): RA = 18:29:00 Dec = +00:30:00"); - assertEquals(StringUtil.htmlArray2textArray(Arrays.asList("be bold!")), Arrays.asList("be bold!")); - assertEquals(StringUtil.htmlArray2textArray(null), Collections.emptyList()); + /** + * Test of isEmpty method, of class StringUtil. + */ + @Test + public void testIsEmpty() { + assertEquals( isValid, StringUtil.isEmpty(inputString) ); + } } - - @Test - public void testNullToEmpty() { - assertEquals( "hello", StringUtil.nullToEmpty("hello") ); - assertEquals( "", StringUtil.nullToEmpty(null) ); + + @RunWith(Parameterized.class) + public static class TestIsAlphaNumeric { + + public boolean isValid; + public String inputString; + + public TestIsAlphaNumeric(boolean isValid, String inputString) { + this.isValid = isValid; + this.inputString = inputString; + } + + @Parameters + public static Collection parameters() { + return Arrays.asList( + new Object[][] { + { true, "abc" }, + { true, "1230" }, + { true, "1230abc" }, + { true, "1230abcABC" }, + { false, "1230abcABC#" }, + } + ); + } + + /** + * Test of isAlphaNumeric method, of class StringUtil. + */ + @Test + public void testIsAlphaNumeric() { + assertEquals( isValid, StringUtil.isAlphaNumeric(inputString) ); + } } - - @Test - public void testSymmetricEncryption() { - String source = "Hello, world! This is an encryption test"; - String password = "12345678"; - final String encrypted = StringUtil.encrypt(source, password); - final String decrypted = StringUtil.decrypt(encrypted, password); + + @RunWith(Parameterized.class) + public static class TestIsAlphaNumericChar { + + public boolean isValid; + public char inputChar; - assertEquals(source, decrypted); + public TestIsAlphaNumericChar(boolean isValid, char inputChar) { + this.isValid = isValid; + this.inputChar = inputChar; + } + + @Parameters + public static Collection parameters() { + return Arrays.asList( + new Object[][] { + { true, 'a' }, + { true, 'f' }, + { true, 'z' }, + { true, '0' }, + { true, '1' }, + { true, '9' }, + { true, 'A' }, + { true, 'G' }, + { true, 'Z' }, + { false, '@' }, + } + ); + } + + /** + * Test of isAlphaNumericChar method, of class StringUtil. + */ + @Test + public void testIsAlphaNumericChar() { + assertEquals( isValid, StringUtil.isAlphaNumericChar(inputChar) ); + } } - - @Test - public void testIsTrue() { - Stream.of("yes", "Yes", " yes ", "1", "allow", "tRuE") - .forEach( v -> assertTrue(StringUtil.isTrue(v)) ); + + public static class StringUtilNoParamTest{ + + @Test + public void testHtml2Text() { + assertEquals(StringUtil.html2text("be bold!"), "be bold!"); + assertEquals(StringUtil.html2text(null), null); + assertEquals(StringUtil.html2text("

Description:
\n" + + "Data were taken May-June 2003 and 2005. Flux units are in mJy per 31 arcsecond beam.\n" + + "

\n" + + "\n" + + "

Telescope Information
\n" + + "Caltech Submillimeter Observatory

\n" + + "\n" + + "

Status:
\n" + + "Final

\n" + + "\n" + + "

Sampling:
\n" + + "Sensitivity: Average 1 sigma rms = 10 mJy per beam.
\n" + + "Waveband: 1120 microns
\n" + + "Resolution: 31 arcsecond beam in 10 arcsecond pixels (diffuse large-scale structure is lost)\n" + + "

\n" + + "\n" + + "

Areal Coverage:
\n" + + "11 square degrees\n" + + "

\n" + + "\n" + + "

Map Center (Galactic):
\n" + + "NA
\n" + + "NA

\n" + + "\n" + + "

Map Center (J2000):
\n" + + "RA = 18:29:00
\n" + + "Dec = +00:30:00

"), "Description: Data were taken May-June 2003 and 2005. Flux units are in mJy per 31 arcsecond beam. Telescope Information Caltech Submillimeter Observatory Status: Final Sampling: Sensitivity: Average 1 sigma rms = 10 mJy per beam. Waveband: 1120 microns Resolution: 31 arcsecond beam in 10 arcsecond pixels (diffuse large-scale structure is lost) Areal Coverage: 11 square degrees Map Center (Galactic): NA NA Map Center (J2000): RA = 18:29:00 Dec = +00:30:00"); + assertEquals(StringUtil.htmlArray2textArray(Arrays.asList("be bold!")), Arrays.asList("be bold!")); + assertEquals(StringUtil.htmlArray2textArray(null), Collections.emptyList()); + } + + @Test + public void testNullToEmpty() { + assertEquals( "hello", StringUtil.nullToEmpty("hello") ); + assertEquals( "", StringUtil.nullToEmpty(null) ); + } - Stream.of("es", "no", " 0 s ", "0", "x", "false") - .forEach( v -> assertFalse(StringUtil.isTrue(v)) ); + @Test + public void testSymmetricEncryption() { + String source = "Hello, world! This is an encryption test"; + String password = "12345678"; + final String encrypted = StringUtil.encrypt(source, password); + final String decrypted = StringUtil.decrypt(encrypted, password); + + assertEquals(source, decrypted); + } - assertFalse( StringUtil.isTrue(null) ); + @Test + public void testIsTrue() { + Stream.of("yes", "Yes", " yes ", "1", "allow", "tRuE") + .forEach( v -> assertTrue(StringUtil.isTrue(v)) ); + + Stream.of("es", "no", " 0 s ", "0", "x", "false") + .forEach( v -> assertFalse(StringUtil.isTrue(v)) ); + + assertFalse( StringUtil.isTrue(null) ); + } } }