From: Steve F. <sm...@us...> - 2002-08-07 21:53:47
|
Update of /cvsroot/mockobjects/no-stone-unturned/doc/xdocs In directory usw-pr-cvs1:/tmp/cvs-serv11406/doc/xdocs Modified Files: random.xml Log Message: added Nat's random chapter Index: random.xml =================================================================== RCS file: /cvsroot/mockobjects/no-stone-unturned/doc/xdocs/random.xml,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- random.xml 7 Aug 2002 17:21:52 -0000 1.1 +++ random.xml 7 Aug 2002 21:53:44 -0000 1.2 @@ -1,161 +1,159 @@ -<?xml version="1.0"?> - <chapter> -<title>Random Acts</title> - -<section> -<title>Introduction</title> - -<remark>Expand this</remark> - -<para> -Pseudo-random behaviour is used in many applications. -In games it is used to portray natural behaviours that are too complex to -simulate accurately, or to add variety to behaviours that are too predictable -when simulated algorithmically. -</para> - -<para> -How do we test randomness? -</para> - -</section> - -<section> -<title>Come Rain...</title> - -<para> -I have been contracted to write a game that simulates the strategy of -Formula One motor racing. -My customer tells me that the weather plays has an important effect on -F1 strategy, so my game will have to simulate the weather somehow. -However, she wants the game to run in a web browser, and that means -writing it as a Java applet. -I'm going to rule out a realistic weather simulation — I'm going to -need all those CPU cycles to simulate the cars and drivers — and -instead randomly generate weather effects. -</para> - -<para> -The first story I have is that the player must choose different tyres -depending on whether it is raining or not. Most of the time it will -be sunny but it should rain on every one out of every five races, -chosen at random. -Time to write a test, but how do I test a random event? -The weather object is obviously going to use a random number generator -to test the probability of rain. If that random number generator is -completely encapsulated within the weather object, I cannot override -the randomness to cause the behaviour I want to test. -Therefore, I need to mock that random number generator in my tests to -feed in fixed values, and pass the random number generator to the -weather object's constructor. -</para> - -<programlisting lang="java">public void testRandomRain() { - MockRandom rng = new MockRandom(); - - Weather weather = new Weather( rng ); - - rng.setNextDouble( 0.0 ); - weather.randomize(); - assertTrue( "is raining", weather.isRaining() ); - - rng.setNextDouble( Weather.CHANCE_OF_RAIN ); - weather.randomize(); - assertTrue( "is not raining", !weather.isRaining() ); - - rng.setNextDouble( 1.0 ); - weather.randomize(); - assertTrue( "is not raining", !weather.isRaining() ); -}</programlisting> + <title>Random Acts</title> -<para> -Here's the <classname>MockRandom</classname> class used by the test: -</para> + <section> + <title>Introduction</title> + <remark>Expand this</remark> + + <para> + Pseudo-random behaviour is used in many applications. + In games it is used to portray natural behaviours that are too complex to + simulate accurately, or to add variety to behaviours that are too predictable + when simulated algorithmically. + </para> + + <para> + How do we test randomness? + </para> + + </section> <!-- Introduction --> + + <section> + <title>Come Rain...</title> + + <para> + I have been contracted to write a game that simulates the strategy of + Formula One motor racing. + My customer tells me that the weather plays has an important effect on + F1 strategy, so my game will have to simulate the weather somehow. + However, she wants the game to run in a web browser, and that means + writing it as a Java applet. + I'm going to rule out a realistic weather simulation — I'm going to + need all those CPU cycles to simulate the cars and drivers — and + instead randomly generate weather effects. + </para> + + <para> + The first story I have is that the player must choose different tyres + depending on whether it is raining or not. Most of the time it will + be sunny but it should rain on every one out of every five races, + chosen at random. + Time to write a test, but how do I test a random event? + The weather object is obviously going to use a random number generator + to test the probability of rain. If that random number generator is + completely encapsulated within the weather object, I cannot override + the randomness to cause the behaviour I want to test. + Therefore, I need to mock that random number generator in my tests to + feed in fixed values, and pass the random number generator to the + weather object's constructor. + </para> + + <programlisting lang="java"> +public void testRandomRain() { + MockRandom rng = new MockRandom(); + + Weather weather = new Weather( rng ); + + rng.setNextDouble( 0.0 ); + weather.randomize(); + assertTrue( "is raining", weather.isRaining() ); + + rng.setNextDouble( Weather.CHANCE_OF_RAIN ); + weather.randomize(); + assertTrue( "is not raining", !weather.isRaining() ); + + rng.setNextDouble( 1.0 ); + weather.randomize(); + assertTrue( "is not raining", !weather.isRaining() ); +}</programlisting> + + <para> + Here's the <classname>MockRandom</classname> class used by the test: + </para> -<programlisting>public class MockRandom - extends Random + <programlisting> +public class MockRandom extends Random { private double nextDouble = 0.0; - + public void setNextDouble( double d ) { nextDouble = d; } - + public double nextDouble() { return nextDouble; } -} -</programlisting> +}</programlisting> -<para> -And now we can write a Weather class that passes the tests: -</para> + <para> + And now we can write a Weather class that passes the tests: + </para> -<programlisting lang="java">public class Weather + <programlisting lang="java"> +public class Weather { public static double CHANCE_OF_RAIN = 0.2; - - + + private Random rng; private boolean isRaining = false; - + public Weather( Random rng ) { this.rng = rng; } - + public boolean isRaining() { return isRaining; } - + public void randomize() { - isRaining = rng.nextDouble() < CHANCE_OF_RAIN; + isRaining = rng.nextDouble() < CHANCE_OF_RAIN; } }</programlisting> -</section> + </section> <!-- Come rain --> + <section> + <title>...Or Shine</title> -<section> -<title>...Or Shine</title> - -<para>My customer now tells me that ground temperature is also -important to race strategy. -The player should choose different tyre compounds depending on the -temperature. Now my weather object must choose a temperature and whether -it is raining, both at random. -The random temperature will be chosen from a range of 20°C and 30°C. -But, the ground should be on average half the temperature when it is raining -compared to when it is sunny. -</para> - -<para> -Again, let's write a test. Actually, I think we need -two tests, one to test that the temperature -is selected from within the forecast range when it is sunny, -and another to test that it is half the sunny temperature when raining. -Let's start with the former. Again we need to mock the random number -generator, but this time the <methodname>randomize</methodname> method -will get <emphasis>two</emphasis> random numbers. We need to change our -<classname>MockRandom</classname> class to mock a stream of random -numbers, rather than just one: -</para> + <para> + My customer now tells me that ground temperature is also + important to race strategy. + The player should choose different tyre compounds depending on the + temperature. Now my weather object must choose a temperature and whether + it is raining, both at random. + The random temperature will be chosen from a range of 20°C and 30°C. + But, the ground should be on average half the temperature when it is raining + compared to when it is sunny. + </para> + + <para> + Again, let's write a test. Actually, I think we need + two tests, one to test that the temperature + is selected from within the forecast range when it is sunny, + and another to test that it is half the sunny temperature when raining. + Let's start with the former. Again we need to mock the random number + generator, but this time the <methodname>randomize</methodname> method + will get <emphasis>two</emphasis> random numbers. We need to change our + <classname>MockRandom</classname> class to mock a stream of random + numbers, rather than just one: + </para> -<programlisting>public class MockRandom - extends Random + <programlisting> +public class MockRandom extends Random { private double[] nextDoubles = {0.0}; private int nextIndex = 0; - + public void setNextDouble( double d ) { setNextDoubles( new double[]{ d } ); } - + public void setNextDoubles( double[] d ) { nextDoubles = d; nextIndex = 0; } - + public double nextDouble() { double result = nextDoubles[nextIndex]; nextIndex = (nextIndex + 1) % nextDoubles.length; @@ -163,18 +161,19 @@ } }</programlisting> -<para> -Ok, the new <classname>MockRandom</classname> doesn't affect our existing -tests, so we can go on to write the test for random temperature when sunny: -</para> + <para> + Ok, the new <classname>MockRandom</classname> doesn't affect our existing + tests, so we can go on to write the test for random temperature when sunny: + </para> -<programlisting>public void testRandomTemperatureSunny() { + <programlisting> +public void testRandomTemperatureSunny() { MockRandom rng = new MockRandom(); final double SUNNY = 1.0; - + Weather weather = new Weather( rng ); - + rng.setNextDoubles( new double[] { SUNNY, 0.0 } ); weather.randomize(); assertEquals( "should be min temperature", @@ -192,56 +191,58 @@ Weather.MAX_TEMPERATURE, weather.getTemperature(), 0.0 ); }</programlisting> -<para> -And write code to pass that test: -</para> + <para> + And write code to pass that test: + </para> -<programlisting>public class Weather + <programlisting> +public class Weather { public static double CHANCE_OF_RAIN = 0.2; <emphasis>public static double MIN_TEMPERATURE = 20; public static double MAX_TEMPERATURE = 30;</emphasis> - + private Random rng; private boolean isRaining = false; <emphasis>private double temperature = MIN_TEMPERATURE;</emphasis> - - public Weather( Random rng ) { - this.rng = rng; + + public Weather( Random aRng ) { + rng = aRng; } - + public boolean isRaining() { return isRaining; } - + <emphasis>public double getTemperature() { return temperature; }</emphasis> - + public void randomize() { - isRaining = rng.nextDouble() < CHANCE_OF_RAIN; - <emphasis>temperature = MIN_TEMPERATURE + + isRaining = rng.nextDouble() < CHANCE_OF_RAIN; + <emphasis>temperature = MIN_TEMPERATURE + rng.nextDouble() * (MAX_TEMPERATURE-MIN_TEMPERATURE);</emphasis> } }</programlisting> -<para> -Now for the temperature when it is raining. The test will look very similar -the the one I just wrote, except that it expect the temperatures to be -half those when sunny. -</para> + <para> + Now for the temperature when it is raining. The test will look very similar + the the one I just wrote, except that it expect the temperatures to be + half those when sunny. + </para> -<programlisting>public void testRandomTemperatureRaining() { + <programlisting> +public void testRandomTemperatureRaining() { MockRandom rng = new MockRandom(); final double RAIN = 0.0; - + Weather weather = new Weather( rng ); - + rng.setNextDoubles( new double[] { RAIN, 0.0 } ); weather.randomize(); assertEquals( "should be min rainy temperature", Weather.MIN_TEMPERATURE/2, weather.getTemperature(), 0.0 ); - + rng.setNextDoubles( new double[] { RAIN, 0.5 } ); weather.randomize(); assertEquals( "should be average rainy temperature", @@ -254,63 +255,67 @@ Weather.MAX_TEMPERATURE/2, weather.getTemperature(), 0.0 ); }</programlisting> -<para> -Now I'll change the <classname>Weather</classname>'s -<methodname>randomize</methodname> to half the temperature when it is raining: -</para> - -<programlisting>public void randomize() { - temperature = MIN_TEMPERATURE + + <para> + Now I'll change the <classname>Weather</classname>'s + <methodname>randomize</methodname> to half the temperature when it is raining: + </para> + + <programlisting> +public void randomize() { + temperature = MIN_TEMPERATURE + rng.nextDouble() * (MAX_TEMPERATURE-MIN_TEMPERATURE); - - isRaining = rng.nextDouble() < CHANCE_OF_RAIN; + + isRaining = rng.nextDouble() < CHANCE_OF_RAIN; if( isRaining ) temperature *= 0.5; }</programlisting> -<para>That was easy! I'll just run my tests and... whoops! The -<methodname>testRandomTemperatureRaining</methodname> test failed. -Not only that, my <methodname>testRandomTemperatureSunny</methodname> test -failed as well! Why did that happen? The behaviour I added to the -<methodname>randomize</methodname> method should not have had an affect -when it was not raining. -</para> - -</section> - -<section> -<title>Test Smell: Order Shouldn't Matter</title> - -<para>Actually, looking at it again, I realise that I swapped the order -of that statements that randomized the rain and temperature. -The tests now initialise the stream of mock random numbers in the wrong -order. This is not good: tests should not be tied to the internal -implementation details of the class. They should specify only its externally -visible behaviour. How can I make my tests less brittle? -</para> - -<para> -There should not be any externally visible dependency between randomising -the temperature and randomising the rain. A way to remove the dependency -is to pass <emphasis>two</emphasis> random number generators to the -<classname>Weather</classname> class, one for the temperature and one for -the rain. My tests can then mock each generator independently to force -a particular outcome, no matter what order the <classname>Weather</classname> -samples the generators. Here's the last test rewritten with two generators: -</para> + <para> + That was easy! I'll just run my tests and... whoops! The + <methodname>testRandomTemperatureRaining</methodname> test failed. + Not only that, my <methodname>testRandomTemperatureSunny</methodname> test + failed as well! Why did that happen? The behaviour I added to the + <methodname>randomize</methodname> method should not have had an affect + when it was not raining. + </para> + + </section> <!-- Come shine --> + + <section> + <title>Test Smell: Order Shouldn't Matter</title> + + <para> + Actually, looking at it again, I realise that I swapped the order + of that statements that randomized the rain and temperature. + The tests now initialise the stream of mock random numbers in the wrong + order. This is not good: tests should not be tied to the internal + implementation details of the class. They should specify only its externally + visible behaviour. How can I make my tests less brittle? + </para> + + <para> + There should not be any externally visible dependency between randomising + the temperature and randomising the rain. A way to remove the dependency + is to pass <emphasis>two</emphasis> random number generators to the + <classname>Weather</classname> class, one for the temperature and one for + the rain. My tests can then mock each generator independently to force + a particular outcome, no matter what order the <classname>Weather</classname> + samples the generators. Here's the last test rewritten with two generators: + </para> -<programlisting>public void testRandomTemperatureRaining() { + <programlisting> +public void testRandomTemperatureRaining() { <emphasis>MockRandom rain_rng = new MockRandom(); rain_rng.setNextDouble(0.0); - + MockRandom temp_rng = new MockRandom(); - + Weather weather = new Weather( rain_rng, temp_rng ); - + temp_rng.setNextDouble( 0.0 );</emphasis> weather.randomize(); assertEquals( "should be min rainy temperature", Weather.MIN_TEMPERATURE/2, weather.getTemperature(), 0.0 ); - + <emphasis>temp_rng.setNextDouble( 0.5 );</emphasis> weather.randomize(); assertEquals( "should be average rainy temperature", @@ -323,158 +328,162 @@ Weather.MAX_TEMPERATURE/2, weather.getTemperature(), 0.0 ); }</programlisting> -<para> -I can rewrite the <methodname>testRandomTemperatureSunny</methodname> -test in a similar way and I also have to change the -<methodname>testRandomRain</methodname> test to instantiate the -<classname>Weather</classname> object with two random number generators; -we'll skip over the code to save trees. -</para> - -<para> -Now I have failing tests for the behaviour I want to implement, so I -need to change my Weather class to use two random number generators: -</para> + <para> + I can rewrite the <methodname>testRandomTemperatureSunny</methodname> + test in a similar way and I also have to change the + <methodname>testRandomRain</methodname> test to instantiate the + <classname>Weather</classname> object with two random number generators; + we'll skip over the code to save trees. + </para> + + <para> + Now I have failing tests for the behaviour I want to implement, so I + need to change my Weather class to use two random number generators: + </para> -<programlisting>public class Weather + <programlisting> +public class Weather { public static double CHANCE_OF_RAIN = 0.2; public static double MIN_TEMPERATURE = 20; // degrees C public static double MAX_TEMPERATURE = 30; // degrees C - + <emphasis>private Random tempRandom, rainRandom;</emphasis> private boolean isRaining = false; private double temperature = MIN_TEMPERATURE; - + <emphasis>public Weather( Random rainRandom, Random tempRandom ) { this.tempRandom = tempRandom; this.rainRandom = rainRandom; }</emphasis> - + [...] - + public void randomize() { - temperature = MIN_TEMPERATURE + + temperature = MIN_TEMPERATURE + <emphasis>tempRandom</emphasis>.nextDouble() * (MAX_TEMPERATURE-MIN_TEMPERATURE); - - isRaining = <emphasis>rainRandom</emphasis>.nextDouble() < CHANCE_OF_RAIN; + + isRaining = <emphasis>rainRandom</emphasis>.nextDouble() < CHANCE_OF_RAIN; if( isRaining ) temperature *= 0.5; } }</programlisting> -<tip> -Only test the order in which an object calls methods of other objects -if that is an important aspect of your object's publically visible -behaviour. If it is unimportant, your tests will be brittle if your -they expect one particular order. -</tip> - -<para> -Finally, my <classname>MockRandom</classname> class now contains behaviour -that I don't use, and that I've realised is a bad idea. I'll discard -that code by restoring the original version of the class from my source -code repository. -</para> - -</section> - - -<section> -<title>Refactoring: Too Many Arguments</title> -<para> -My customer is happy. However, she tells me, in Formula One, teams -use tyres with different treads depending on how wet the track is, so -our weather class needs to simulate both if it is raining, and -how wet the track is. Another important element of Formula One strategy -is changing tyres when the weather changes, so the rain should start and -stop at random intervals, and the track should become wetter when it is -raining and dry off when it is sunny. -</para> - -<para> -All this is straightforward to implement, but my nose is twitching: -my code, although functional, is smelly. -I can foresee that as I add functionality to the -<classname>Weather</classname> class, the implementation will become -increasingly awkward because there will be too many random number generators. -In particular each time I add more random behaviour I will have to change -all the tests because the signature of the constructor will have changed, -and the constructor will end up with far too many parameters: -</para> + <tip> + Only test the order in which an object calls methods of other objects + if that is an important aspect of your object's publically visible + behaviour. If it is unimportant, your tests will be brittle if your + they expect one particular order. + </tip> + + <para> + Finally, my <classname>MockRandom</classname> class now contains behaviour + that I don't use, and that I've realised is a bad idea. I'll discard + that code by restoring the original version of the class from my source + code repository. + </para> + + </section> <!-- Test Smell --> + + <section> + <title>Refactoring: Too Many Arguments</title> + <para> + My customer is happy. However, she tells me, in Formula One, teams + use tyres with different treads depending on how wet the track is, so + our weather class needs to simulate both if it is raining, and + how wet the track is. Another important element of Formula One strategy + is changing tyres when the weather changes, so the rain should start and + stop at random intervals, and the track should become wetter when it is + raining and dry off when it is sunny. + </para> + + <para> + All this is straightforward to implement, but my nose is twitching: + my code, although functional, is smelly. + I can foresee that as I add functionality to the + <classname>Weather</classname> class, the implementation will become + increasingly awkward because there will be too many random number generators. + In particular each time I add more random behaviour I will have to change + all the tests because the signature of the constructor will have changed, + and the constructor will end up with far too many parameters: + </para> -<programlisting>public Weather( Random rainRandom, Random tempRandom, Random wetnessRandom, + <programlisting> +public Weather( Random rainRandom, Random tempRandom, Random wetnessRandom, Random rainDurationRandom, Random dryDurationRandom ) { [...] }</programlisting> -<para> -I recognise this "code smell". Every time I see a method that takes a lot -of arguments, I know that there is a new concept waiting to be extracted. -Just as the method -<methodname>drawRectangle( int x, int y, int width, int height )</methodname> -indicates that a Rectangle class should be factored out and the method -replaced by <methodname>draw( Rectangle r )</methodname>, so all those -<classname>Random</classname> arguments indicate that there is an -weather-specific source of randomness waiting to be extracted. -It's time to don my refactoring hat and clear up this mess now; -leaving it any later will just make more work. The first thing I need -to do is define an interface for a weather-specific source of randomness: -</para> + <para> + I recognise this "code smell". Every time I see a method that takes a lot + of arguments, I know that there is a new concept waiting to be extracted. + Just as the method + <methodname>drawRectangle( int x, int y, int width, int height )</methodname> + indicates that a Rectangle class should be factored out and the method + replaced by <methodname>draw( Rectangle r )</methodname>, so all those + <classname>Random</classname> arguments indicate that there is an + weather-specific source of randomness waiting to be extracted. + It's time to don my refactoring hat and clear up this mess now; + leaving it any later will just make more work. The first thing I need + to do is define an interface for a weather-specific source of randomness: + </para> -<programlisting>public interface WeatherRandom + <programlisting> +public interface WeatherRandom { boolean nextIsRaining(); double nextTemperature(); }</programlisting> -<para> -I don't have to implement this interface right now. -I can test the <classname>Weather</classname> class by mocking the interface, -and then test and write an implementation once the -<classname>Weather</classname> tests are passing. -Our new <classname>WeatherRandom</classname> class simplifies our tests -greatly. We no longer have to test that the <classname>Weather</classname> -class compares probabilities against random numbers correctly; that will -be the responsibility of the real implementation of -<classname>WeatherRandom</classname>. Instead we just have to test that -the <classname>Weather</classname> class stores the random weather and -calculates a cooler temperature when it is raining. Here's our -<methodname>testRandomTemperatureRaining</methodname> test: -</para> + <para> + I don't have to implement this interface right now. + I can test the <classname>Weather</classname> class by mocking the interface, + and then test and write an implementation once the + <classname>Weather</classname> tests are passing. + Our new <classname>WeatherRandom</classname> class simplifies our tests + greatly. We no longer have to test that the <classname>Weather</classname> + class compares probabilities against random numbers correctly; that will + be the responsibility of the real implementation of + <classname>WeatherRandom</classname>. Instead we just have to test that + the <classname>Weather</classname> class stores the random weather and + calculates a cooler temperature when it is raining. Here's our + <methodname>testRandomTemperatureRaining</methodname> test: + </para> -<programlisting>public void testRandomTemperatureRaining() { + <programlisting> +public void testRandomTemperatureRaining() { final double TEMPERATURE = 20; - + MockWeatherRandom rng = new MockWeatherRandom() { public boolean nextIsRaining() { return true; } public double nextTemperature() { return TEMPERATURE; } }; - + Weather weather = new Weather( rng ); - + weather.randomize(); - assertEquals( "temperature", + assertEquals( "temperature", TEMPERATURE/2.0, weather.getTemperature(), 0.0 ); }</programlisting> -<para> -Now I have to modify the <classname>Weather</classname> class to -use a <classname>WeatherRandom</classname> to pass the tests: -</para> + <para> + Now I have to modify the <classname>Weather</classname> class to + use a <classname>WeatherRandom</classname> to pass the tests: + </para> -<programlisting>public class Weather + <programlisting> +public class Weather { <emphasis>private WeatherRandom random;</emphasis> private boolean isRaining = false; private double temperature = 0.0; - + <emphasis>public Weather( WeatherRandom random ) { this.random= random; }</emphasis> - + [...] - + public void randomize() { <emphasis>temperature = random.nextTemperature(); isRaining = random.nextIsRaining();</emphasis> @@ -482,21 +491,22 @@ } }</programlisting> -<para> -And finally I need to test and implement a real -<classname>WeatherRandom</classname> that generates random weather -using a random number generator. -Each of the <classname>WeatherRandom</classname> methods can be tested -individually using a MockRandom object, just as we did in our earlier -tests of the <classname>Weather</classname> class. Because each method -makes one call to the random number generator, no internal -implementation details leak out into our tests. -</para> + <para> + And finally I need to test and implement a real + <classname>WeatherRandom</classname> that generates random weather + using a random number generator. + Each of the <classname>WeatherRandom</classname> methods can be tested + individually using a MockRandom object, just as we did in our earlier + tests of the <classname>Weather</classname> class. Because each method + makes one call to the random number generator, no internal + implementation details leak out into our tests. + </para> -<programlisting>public void testNextIsRaining() { + <programlisting> +public void testNextIsRaining() { MockRandom rng = new MockRandom(); WeatherRandom weather_random = new DefaultWeatherRandom(rng); - + rng.setNextDouble( 0.0 ); assertTrue( "is raining", weather_random.nextIsRaining() ); @@ -507,15 +517,16 @@ assertTrue( "is not raining", !weather_random.nextIsRaining() ); }</programlisting> -<para> -I can now go on to implement the additional random behaviour requested -by my customer. I will define each additional random effect as a method -in the <classname>WeatherRandom</classname> interface. I will also define -a sensible default result in the <classname>MockWeatherRandom</classname> -so that tests that do not care about the effect do not have to be changed. -</para> + <para> + I can now go on to implement the additional random behaviour requested + by my customer. I will define each additional random effect as a method + in the <classname>WeatherRandom</classname> interface. I will also define + a sensible default result in the <classname>MockWeatherRandom</classname> + so that tests that do not care about the effect do not have to be changed. + </para> -<programlisting>public interface WeatherRandom + <programlisting> +public interface WeatherRandom { boolean nextIsRaining(); double nextTemperature(); @@ -524,34 +535,34 @@ double nextDryDuration();</emphasis> }</programlisting> -</section> + </section> <!-- Too many arguments --> -<section> -<title>What Have We Learned?</title> + <section> + <title>What Have We Learned?</title> -<remark>Expand this</remark> + <remark>Expand this</remark> -<para> -Test random behaviour by pulling the random number generator out of the -object being tested and mocking it. -</para> - -<para> -Mocking random behaviour can expose implementation details by assuming -how individual elements of a random number sequence will be used by -the class under test. This breaks encapsulation and makes tests brittle. -Avoid brittle tests by "parallelizing" your random number streams. -Initialise your objects with multiple sources of randomness that can be -mocked independently to test specific behaviours. -</para> - -<para> -Define application-specific random generators, rather than passing multiple -random number generators into your class, so that changes to the random -behaviour of your class do not cause changes to ripple through your class -and all of its tests. -</para> + <para> + Test random behaviour by pulling the random number generator out of the + object being tested and mocking it. + </para> + + <para> + Mocking random behaviour can expose implementation details by assuming + how individual elements of a random number sequence will be used by + the class under test. This breaks encapsulation and makes tests brittle. + Avoid brittle tests by "parallelizing" your random number streams. + Initialise your objects with multiple sources of randomness that can be + mocked independently to test specific behaviours. + </para> + + <para> + Define application-specific random generators, rather than passing multiple + random number generators into your class, so that changes to the random + behaviour of your class do not cause changes to ripple through your class + and all of its tests. + </para> -</section> + </section> <!-- what have we learned --> </chapter> |