From 0d7701868eb544d91bc806acd7305cd696a3cd63 Mon Sep 17 00:00:00 2001 From: Martin Goik <goik@hdm-stuttgart.de> Date: Mon, 4 Dec 2017 08:24:54 +0100 Subject: [PATCH] Unit test explanation supplements --- Doc/Sd1/objectsClasses.xml | 160 +++++++++++++++++++++++++++---------- 1 file changed, 119 insertions(+), 41 deletions(-) diff --git a/Doc/Sd1/objectsClasses.xml b/Doc/Sd1/objectsClasses.xml index 7213a8963..3d5cf619d 100644 --- a/Doc/Sd1/objectsClasses.xml +++ b/Doc/Sd1/objectsClasses.xml @@ -8736,11 +8736,79 @@ After printDuplicateValue: <emphasis role="red">6</emphasis></screen></td> to a given specification. Consider an example:</para> <figure xml:id="sd1_fig_methodSpecification"> - <title>Method specification</title> + <title>Informal problem specification</title> + + <para>Consider a sequence (a,b) of two integer values. The result shall + be:<itemizedlist> + <listitem> + <para>a, if a is negative.</para> + </listitem> + + <listitem> + <para>b, if a is non-negative and b is negative.</para> + </listitem> + + <listitem> + <para>a, if both a and b are non-negative.</para> + </listitem> + </itemizedlist></para> + </figure> + + <figure xml:id="sd1_fig_SpecificationExamples"> + <title>Illustrating examples</title> + + <para>Consider a sequence (a,b) of two integer values. The result shall + be:<informaltable border="1"> + <tr> + <th>Input</th> + + <th>Expected output</th> + </tr> + + <tr> + <td valign="top">(12, 4)</td> + + <td valign="top">12</td> + </tr> + + <tr> + <td valign="top">( 0, 4)</td> + + <td valign="top">0</td> + </tr> + + <tr> + <td valign="top">( -1, 4)</td> + + <td valign="top">-1</td> + </tr> + + <tr> + <td valign="top">( 4, -3)</td> + + <td valign="top">-3</td> + </tr> + + <tr> + <td valign="top">( -3, -8)</td> + + <td valign="top">-3</td> + </tr> + + <tr> + <td valign="top">( -9, -2)</td> + + <td valign="top">-9</td> + </tr> + </informaltable></para> + </figure> + + <figure xml:id="sd1_fig_methodSpecification"> + <title><xref linkend="glo_Javadoc"/> method specification</title> <programlisting language="java">public class Helper { /** - * Find the first of two negative values. Example: + * Find the first of two negative values if present. Example: * Having a == 3 and b == -1 results in -1. * * @param a first value @@ -8749,13 +8817,14 @@ After printDuplicateValue: <emphasis role="red">6</emphasis></screen></td> * negative. The first negative parameter's value otherwise. */ static public int getFirstNegative(int a, int b) { - return 1234; // TODO: Implement me correctly + <emphasis role="red">return 1234</emphasis>; // TODO: Implement me correctly } }</programlisting> </figure> - <para>The method <methodname>getFirstNegative(...)</methodname> may be - executed from an arbitrary context:</para> + <para>The <code>static</code> method + <methodname>getFirstNegative(...)</methodname> may be executed from an + arbitrary context:</para> <figure xml:id="sd1_fig_firstNegMethodExecFrmMain"> <title>Execution using <methodname>main(...)</methodname></title> @@ -8770,10 +8839,10 @@ After printDuplicateValue: <emphasis role="red">6</emphasis></screen></td> </figure> <para>This wrong result is due to our yet flawed implementation. Before - correcting the error we set up some unit tests:</para> + correcting the error we set up a unit test:</para> <figure xml:id="sd1_fig_firstNegTestSpecExample"> - <title>Specification example test</title> + <title><xref linkend="glo_Junit"/> based specification test</title> <programlisting language="java">/** * Testing {@link Helper#getFirstNegative(int, int)} @@ -8817,29 +8886,32 @@ public class HelperTest { <para>Before finally correcting the implementation we add some more tests beforehand:</para> - <figure xml:id="sd1_fig_fistNegFurtherTests"> - <title>Testing other negatives</title> + <figure xml:id="sd1_fig_firstNegTestSpecMoreExamples"> + <title>More tests</title> - <programlisting language="java">/** - * Testing other values - */ -@Test - public void testOther() { - Assert.assertEquals(-5, Helper.getFirstNegative(-5, 4)); - Assert.assertEquals(-4, Helper.getFirstNegative(0, -4)); + <programlisting language="java">@Test public void testNoNegative() { + Assert.assertEquals(12, Helper.getFirstNegative(12, 4)); + Assert.assertEquals(0, Helper.getFirstNegative(0, 4)); +} +@Test public void testOther() { + Assert.assertEquals(-1, Helper.getFirstNegative(-1, 4)); + Assert.assertEquals(-2, Helper.getFirstNegative(4, -3)); +} +@Test public void testNegatives() { + Assert.assertEquals(-3, Helper.getFirstNegative(-3, -8)); + Assert.assertEquals(-9, Helper.getFirstNegative(-9, -2)); }</programlisting> </figure> - <figure xml:id="sd1_fig_firstNegNoNegative"> - <title>Testing non-negatives</title> + <figure xml:id="sd1_fig_firstNegTestSpecMassExamples"> + <title>Mass test examples</title> - <programlisting language="java">/** - * Testing non-negative values - */ -@Test -public void testNoNegative() { - Assert.assertEquals(0, Helper.getFirstNegative(0, 1)); - Assert.assertEquals(4, Helper.getFirstNegative(4, 0)); + <programlisting language="java">@Test public void testPositives() { + for (int i = -1, j = 0; -100 < i; i--, j++) { + Assert.assertEquals(i, Helper.getFirstNegative(i, j)); + Assert.assertEquals(i, Helper.getFirstNegative(j, i)); + Assert.assertEquals(j + 2, Helper.getFirstNegative(j + 2, j)); + } }</programlisting> </figure> @@ -9119,21 +9191,28 @@ static public int getFirstNegative(int a, int b) { <programlisting language="java">long start = System.nanoTime(); System.out.println("1 + 2 + ... + 65535" + "=" + getSum(65535)); long end = System.nanoTime(); -System.out.println("Elapsed time: " + (end - start) + " nanoseconds"); -</programlisting> +System.out.println("Elapsed time: " + (end - start) + " nanoseconds");</programlisting> <screen>1 + 2 + ... + 65535=2147450880 Elapsed time: 1169805 nanoseconds</screen> - <para>Barely more than one millisecond this seems to be - acceptable. But using the method for calculations inside some - tight loop this might have a serious negative performance - impact.</para> + <para>Barely more than one millisecond seems to be acceptable. But + using the method for calculations inside some tight loop this + might have a serious negative performance impact.</para> <para>Thus implement a better (quicker) solution avoiding the loop by using the explicit form. When you are finished re-estimate execution time and compare the result to the previous solution.</para> + + <para>Provide unit tests and take care of larger values. What is + the largest possible value? Test it as well!</para> + + <tip> + <para>Read <quote + xlink:href="https://betterexplained.com/articles/techniques-for-adding-the-numbers-1-to-100">Techniques + for Adding the Numbers 1 to 100</quote>.</para> + </tip> </question> <answer> @@ -9145,8 +9224,8 @@ Elapsed time: 1169805 nanoseconds</screen> <para role="eclipse">Sd1/summing/V2</para> </annotation> - <para>Since only our implementation changes we can still use the - same unit tests. Our first straightforward implementation attempt + <para>Since only our implementation changes we reuse our existing + unit tests. Our first straightforward implementation attempt reads:</para> <programlisting language="java" linenumbering="unnumbered">public static long getSum (int limit) { @@ -9164,8 +9243,7 @@ Actual :10</screen> <para>We forgot to deal with negative <code>limit</code> values. Our sum is supposed to start with 0 so negative <code>limit</code> - values should yield 0 as result like in our loop based - solution:</para> + values should yield 0 like in our loop based solution:</para> <programlisting language="java">public static long getSum(int limit) { if (limit < 0) { @@ -9175,7 +9253,7 @@ Actual :10</screen> } }</programlisting> - <para>This works better but one test still fails:</para> + <para>This helps but one test still fails:</para> <programlisting language="java">assertEquals(2147450880, Summing.getSumUsingGauss(65535));</programlisting> @@ -9183,8 +9261,8 @@ Actual :10</screen> Expected :2147450880 Actual :-32768</screen> - <para>This is actually a showstopper for large <code>limit</code> - values. The algebraic value of <code>limit * (limit + 1) / + <para>This actually is a showstopper for large <code>limit</code> + values: The algebraic value of <code>limit * (limit + 1) / 2</code> might still fit into an <code>int</code>. But <code>limit * (limit + 1)</code> itself not yet divided by 2 may exceed <code xlink:href="https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#MAX_VALUE">Integer.MAX_VALUE</code>. @@ -9226,9 +9304,9 @@ Actual :-32768</screen> <screen>1 + 2 + ... + 65535=2147450880 Elapsed time: 25422 nanoseconds</screen> - <para>Execution is roughly 46 times faster compared to the loop - based approach. This is not surprising since loop execution is - expensive with respect to performance.</para> + <para>Thus execution is roughly 46 times faster compared to the + loop based approach. This is not surprising since loop execution + is expensive in terms of performance.</para> </answer> </qandaentry> </qandadiv> -- GitLab