You can subscribe to this list here.
2001 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(13) |
Aug
(151) |
Sep
(21) |
Oct
(6) |
Nov
(70) |
Dec
(8) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2002 |
Jan
(47) |
Feb
(66) |
Mar
(23) |
Apr
(115) |
May
(24) |
Jun
(53) |
Jul
(10) |
Aug
(279) |
Sep
(84) |
Oct
(149) |
Nov
(138) |
Dec
(52) |
2003 |
Jan
(22) |
Feb
(20) |
Mar
(29) |
Apr
(106) |
May
(170) |
Jun
(122) |
Jul
(70) |
Aug
(64) |
Sep
(27) |
Oct
(71) |
Nov
(49) |
Dec
(9) |
2004 |
Jan
(7) |
Feb
(38) |
Mar
(3) |
Apr
(9) |
May
(22) |
Jun
(4) |
Jul
(1) |
Aug
(2) |
Sep
(2) |
Oct
|
Nov
(15) |
Dec
(2) |
2005 |
Jan
(1) |
Feb
(1) |
Mar
|
Apr
(1) |
May
(28) |
Jun
(3) |
Jul
(11) |
Aug
(5) |
Sep
(1) |
Oct
(5) |
Nov
(2) |
Dec
(3) |
2006 |
Jan
(8) |
Feb
(3) |
Mar
(8) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: Griffin C. <gri...@ho...> - 2002-10-06 23:57:08
|
I see that there is a NMock project in the mockobject-java source tree. Darren Hobbs and I are already working on a .NET Mock Objects project on sourceforge (http://www.sourceforge.net/projects/dotnetmock). We already have the core framework implemented. Our first release will be this week, once we polish the build and documentation process. After that we are hoping for community input as to what to implement next. Is NMock an exact replication of the java mock objects? If so, can I suggest that we combine our efforts, so as not to create competing .NET Mock Object Implementations? Cheers, Griffin _________________________________________________________________ Send and receive Hotmail on your mobile device: http://mobile.msn.com |
From: Joe W. <joe...@us...> - 2002-10-06 23:14:29
|
Update of /cvsroot/mockobjects/nmock/sample/random In directory usw-pr-cvs1:/tmp/cvs-serv6524/sample/random Modified Files: Weather.cs WeatherTest.cs Log Message: Fixed skewy formatting and made code more .NETish Index: Weather.cs =================================================================== RCS file: /cvsroot/mockobjects/nmock/sample/random/Weather.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- Weather.cs 6 Oct 2002 21:37:45 -0000 1.1 +++ Weather.cs 6 Oct 2002 23:14:27 -0000 1.2 @@ -12,14 +12,14 @@ this.random = random; } - public bool IsRaining() + public bool IsRaining { - return isRaining; + get { return isRaining; } } - public double GetTemperature() + public double Temperature { - return temperature; + get { return temperature; } } public void Randomize() Index: WeatherTest.cs =================================================================== RCS file: /cvsroot/mockobjects/nmock/sample/random/WeatherTest.cs,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- WeatherTest.cs 6 Oct 2002 21:37:45 -0000 1.1 +++ WeatherTest.cs 6 Oct 2002 23:14:27 -0000 1.2 @@ -10,46 +10,51 @@ private IMock random; private Weather weather; - - [SetUp] - public void SetUp() { + + [SetUp] + public void SetUp() + { random = new DynamicMock(typeof(WeatherRandom)); - weather = new Weather((WeatherRandom)random.Object); - } - - [Test] - public void RandomRaining() { + weather = new Weather((WeatherRandom)random.Object); + } + + [Test] + public void RandomRaining() + { random.ExpectAndReturn("NextTemperature", 1.0); random.ExpectAndReturn("NextIsRaining", true); weather.Randomize(); - Assertion.Assert("is raining", weather.IsRaining()); + Assertion.Assert("is raining", weather.IsRaining); } - - [Test] - public void RandomNotRaining() { + + [Test] + public void RandomNotRaining() + { random.ExpectAndReturn("NextTemperature", 1.0); random.ExpectAndReturn("NextIsRaining", false); weather.Randomize(); - Assertion.Assert("is not raining", !weather.IsRaining()); + Assertion.Assert("is not raining", !weather.IsRaining); } - - [Test] - public void RandomTemperatureSunny() { + + [Test] + public void RandomTemperatureSunny() + { double TEMPERATURE = 20.0; random.ExpectAndReturn("NextTemperature", TEMPERATURE); random.ExpectAndReturn("NextIsRaining", false); weather.Randomize(); - Assertion.AssertEquals("temperature", TEMPERATURE, weather.GetTemperature()); - } + Assertion.AssertEquals("temperature", TEMPERATURE, weather.Temperature); + } - [Test] - public void RandomTemperatureRaining() { + [Test] + public void RandomTemperatureRaining() + { double TEMPERATURE = 20.0; random.ExpectAndReturn("NextTemperature", TEMPERATURE); random.ExpectAndReturn("NextIsRaining", true); weather.Randomize(); - Assertion.AssertEquals("temperature", TEMPERATURE / 2.0, weather.GetTemperature()); - } + Assertion.AssertEquals("temperature", TEMPERATURE / 2.0, weather.Temperature); + } } |
From: Joe W. <joe...@us...> - 2002-10-06 23:11:43
|
Update of /cvsroot/mockobjects/nmock/test/NMock/Dynamic In directory usw-pr-cvs1:/tmp/cvs-serv5221/test/NMock/Dynamic Modified Files: ClassGeneratorTest.cs Log Message: Bytecode now emitted for property setters/getters Index: ClassGeneratorTest.cs =================================================================== RCS file: /cvsroot/mockobjects/nmock/test/NMock/Dynamic/ClassGeneratorTest.cs,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- ClassGeneratorTest.cs 6 Oct 2002 20:49:09 -0000 1.1.1.1 +++ ClassGeneratorTest.cs 6 Oct 2002 23:11:39 -0000 1.2 @@ -22,6 +22,9 @@ bool boolReturn(); double doubleReturn(); IThingy AThingy(); + string ReadProperty { get; } + string WriteProperty { set; } + string AProperty { get; set; } } [TestFixture] @@ -161,6 +164,40 @@ IList l = new ArrayList(); mock.Expect("WithOtherArgs", new IsEqual(6), new IsEqual(true), new IsNull(), new IsEqual(l)); thingy.WithOtherArgs(6, true, null, l); + mock.Verify(); + } + + [Test] + public void CallReadOnlyProperty() + { + mock.ExpectAndReturn("ReadProperty", "hello"); + mock.ExpectAndReturn("ReadProperty", "world"); + Assertion.AssertEquals("hello", thingy.ReadProperty); + Assertion.AssertEquals("world", thingy.ReadProperty); + mock.Verify(); + } + + [Test] + public void WriteOnlyPropertyExpectations() + { + mock.Expect("WriteProperty", "hello"); + mock.Expect("WriteProperty", "world"); + thingy.WriteProperty = "hello"; + thingy.WriteProperty = "world"; + mock.Verify(); + } + + [Test] + public void ReadAndWriteProperty() + { + mock.Expect("AProperty", "hello"); + mock.Expect("AProperty", "world"); + mock.ExpectAndReturn("AProperty", "good"); + mock.ExpectAndReturn("AProperty", "bye"); + thingy.AProperty = "hello"; + thingy.AProperty = "world"; + Assertion.AssertEquals("good", thingy.AProperty); + Assertion.AssertEquals("bye", thingy.AProperty); mock.Verify(); } |
From: Joe W. <joe...@us...> - 2002-10-06 23:11:43
|
Update of /cvsroot/mockobjects/nmock/src/NMock/Dynamic In directory usw-pr-cvs1:/tmp/cvs-serv5221/src/NMock/Dynamic Modified Files: ClassGenerator.cs Log Message: Bytecode now emitted for property setters/getters Index: ClassGenerator.cs =================================================================== RCS file: /cvsroot/mockobjects/nmock/src/NMock/Dynamic/ClassGenerator.cs,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- ClassGenerator.cs 6 Oct 2002 20:49:08 -0000 1.1.1.1 +++ ClassGenerator.cs 6 Oct 2002 23:11:39 -0000 1.2 @@ -36,19 +36,16 @@ Type proxyType = typeBuilder.CreateType(); GeneratedBase result = (GeneratedBase)Activator.CreateInstance(proxyType); result.UnderlyingMock = mock; - assemblyBuilder.Save("x.dll"); return result; } - AssemblyBuilder assemblyBuilder; - private TypeBuilder getTypeBuilder(string name, Type implementsInterface) { AppDomain appDomain = AppDomain.CurrentDomain; AssemblyName assemblyName = new AssemblyName(); assemblyName.Name = "DynamicMockAssembly"; - /*AssemblyBuilder*/ assemblyBuilder = appDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave, "c:\\temp\\"); - ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MockModule", "t.dll"); + AssemblyBuilder assemblyBuilder = appDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); + ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MockModule"); return moduleBuilder.DefineType(name, TypeAttributes.Public, typeof(GeneratedBase), new Type[]{implementsInterface}); } @@ -72,8 +69,14 @@ il.DeclareLocal(param.ParameterType); } + string methodName = m.Name; + if (methodName.StartsWith("get_") || methodName.StartsWith("set_")) + { + methodName = methodName.Substring(4); + } + il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldstr, m.Name); + il.Emit(OpCodes.Ldstr, methodName); il.Emit(OpCodes.Ldc_I4_S, paramTypes.Length); il.Emit(OpCodes.Newarr, typeof(object)); |
From: Joe W. <joe...@us...> - 2002-10-06 22:57:51
|
Update of /cvsroot/mockobjects/nmock/src/NMock In directory usw-pr-cvs1:/tmp/cvs-serv30372/src/NMock Modified Files: IMock.cs Mock.cs Log Message: Added Mock.SetValue() for setting up methods/properties that always return the same value and have no expectations about how they are called (i.e. getter methods). Index: IMock.cs =================================================================== RCS file: /cvsroot/mockobjects/nmock/src/NMock/IMock.cs,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- IMock.cs 6 Oct 2002 20:49:08 -0000 1.1.1.1 +++ IMock.cs 6 Oct 2002 22:57:48 -0000 1.2 @@ -37,6 +37,13 @@ void ExpectAndThrow(string methodName, Exception exceptionVal, params object[] args); /// <summary> + /// Set a fixed return value for a method/property. This allows the method to be + /// called multiple times in no particular sequence and have the same value returned + /// each time. Useful for getter style methods. + /// </summary> + void SetValue(string methodName, object returnVal); + + /// <summary> /// Make a call to a mocked up method name to check it meets its expectations. /// </summary> object Call(string methodName, params object[] args); Index: Mock.cs =================================================================== RCS file: /cvsroot/mockobjects/nmock/src/NMock/Mock.cs,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- Mock.cs 6 Oct 2002 20:49:08 -0000 1.1.1.1 +++ Mock.cs 6 Oct 2002 22:57:48 -0000 1.2 @@ -12,11 +12,13 @@ private string name; private object obj; private IList expectations; + private IDictionary values; public Mock(string name) { this.name = name; expectations = new ArrayList(); + values = new Hashtable(); currentExpectation = 0; } @@ -52,8 +54,17 @@ expectations.Add(new Expectation(methodName, null, e, args)); } + public virtual void SetValue(string methodName, object returnVal) + { + values[methodName] = returnVal; + } + public virtual object Call(string methodName, params object[] args) { + if (values.Contains(methodName)) + { + return values[methodName]; + } if (currentExpectation == expectations.Count) { Assertion.Fail(methodName + "() called too many times"); |
From: Joe W. <joe...@us...> - 2002-10-06 22:57:51
|
Update of /cvsroot/mockobjects/nmock/test/NMock In directory usw-pr-cvs1:/tmp/cvs-serv30372/test/NMock Modified Files: MockTest.cs Log Message: Added Mock.SetValue() for setting up methods/properties that always return the same value and have no expectations about how they are called (i.e. getter methods). Index: MockTest.cs =================================================================== RCS file: /cvsroot/mockobjects/nmock/test/NMock/MockTest.cs,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- MockTest.cs 6 Oct 2002 20:49:09 -0000 1.1.1.1 +++ MockTest.cs 6 Oct 2002 22:57:48 -0000 1.2 @@ -195,6 +195,27 @@ } [Test] + public void FixedValue() + { + mock.SetValue("myMethod", "hello"); + mock.SetValue("anotherMethod", "world"); + Assertion.AssertEquals("hello", mock.Call("myMethod")); + Assertion.AssertEquals("hello", mock.Call("myMethod")); + Assertion.AssertEquals("hello", mock.Call("myMethod")); + Assertion.AssertEquals("hello", mock.Call("myMethod")); + Assertion.AssertEquals("world", mock.Call("anotherMethod")); + Assertion.AssertEquals("world", mock.Call("anotherMethod")); + mock.SetValue("myMethod", "bye"); + Assertion.AssertEquals("bye", mock.Call("myMethod")); + Assertion.AssertEquals("bye", mock.Call("myMethod")); + Assertion.AssertEquals("world", mock.Call("anotherMethod")); + mock.SetValue("myMethod", null); + Assertion.AssertNull(mock.Call("myMethod")); + Assertion.AssertNull(mock.Call("myMethod")); + mock.Verify(); + } + + [Test] [ExpectedException(typeof(System.IO.IOException))] public void CallThrowingException() { |
From: Joe W. <joe...@us...> - 2002-10-06 21:37:48
|
Update of /cvsroot/mockobjects/nmock/sample/random In directory usw-pr-cvs1:/tmp/cvs-serv29083/sample/random Added Files: Weather.cs WeatherTest.cs Log Message: Part of the WeatherRandom sample added. --- NEW FILE: Weather.cs --- namespace NMockSample.Random { public class Weather { private WeatherRandom random; private bool isRaining = false; private double temperature = 0.0; public Weather( WeatherRandom random ) { this.random = random; } public bool IsRaining() { return isRaining; } public double GetTemperature() { return temperature; } public void Randomize() { temperature = random.NextTemperature(); isRaining = random.NextIsRaining(); if( isRaining ) temperature *= 0.5; } } public interface WeatherRandom { bool NextIsRaining(); double NextTemperature(); } public class DefaultWeatherRandom : WeatherRandom { private const double CHANCE_OF_RAIN = 0.2; private const double MIN_TEMPERATURE = 20; private const double MAX_TEMPERATURE = 30; private const double TEMPERATURE_RANGE = (MAX_TEMPERATURE-MIN_TEMPERATURE); private System.Random rng; public DefaultWeatherRandom( System.Random rng ) { this.rng = rng; } public bool NextIsRaining() { return rng.NextDouble() < CHANCE_OF_RAIN; } public double NextTemperature() { return MIN_TEMPERATURE + rng.NextDouble() * TEMPERATURE_RANGE; } } } --- NEW FILE: WeatherTest.cs --- using NUnit.Framework; using NMock; namespace NMockSample.Random { [TestFixture] public class WeatherTest { private IMock random; private Weather weather; [SetUp] public void SetUp() { random = new DynamicMock(typeof(WeatherRandom)); weather = new Weather((WeatherRandom)random.Object); } [Test] public void RandomRaining() { random.ExpectAndReturn("NextTemperature", 1.0); random.ExpectAndReturn("NextIsRaining", true); weather.Randomize(); Assertion.Assert("is raining", weather.IsRaining()); } [Test] public void RandomNotRaining() { random.ExpectAndReturn("NextTemperature", 1.0); random.ExpectAndReturn("NextIsRaining", false); weather.Randomize(); Assertion.Assert("is not raining", !weather.IsRaining()); } [Test] public void RandomTemperatureSunny() { double TEMPERATURE = 20.0; random.ExpectAndReturn("NextTemperature", TEMPERATURE); random.ExpectAndReturn("NextIsRaining", false); weather.Randomize(); Assertion.AssertEquals("temperature", TEMPERATURE, weather.GetTemperature()); } [Test] public void RandomTemperatureRaining() { double TEMPERATURE = 20.0; random.ExpectAndReturn("NextTemperature", TEMPERATURE); random.ExpectAndReturn("NextIsRaining", true); weather.Randomize(); Assertion.AssertEquals("temperature", TEMPERATURE / 2.0, weather.GetTemperature()); } } } |
From: Joe W. <joe...@us...> - 2002-10-06 21:36:37
|
Update of /cvsroot/mockobjects/nmock/sample/random In directory usw-pr-cvs1:/tmp/cvs-serv28833/sample/random Log Message: Directory /cvsroot/mockobjects/nmock/sample/random added to the repository |
From: Joe W. <joe...@us...> - 2002-10-06 21:36:26
|
Update of /cvsroot/mockobjects/nmock/sample In directory usw-pr-cvs1:/tmp/cvs-serv28776/sample Log Message: Directory /cvsroot/mockobjects/nmock/sample added to the repository |
From: Joe W. <joe...@us...> - 2002-10-06 20:49:13
|
Update of /cvsroot/mockobjects/nmock In directory usw-pr-cvs1:/tmp/cvs-serv11129 Log Message: Initial import NMock - simple MockObjects library for .NET Status: Vendor Tag: nmock Release Tags: initial N nmock/README.txt N nmock/src/NMock/DynamicMock.cs N nmock/src/NMock/IMock.cs N nmock/src/NMock/IPredicate.cs N nmock/src/NMock/Mock.cs N nmock/src/NMock/Predicates.cs N nmock/src/NMock/Dynamic/ClassGenerator.cs N nmock/test/NMock/DynamicMockTest.cs N nmock/test/NMock/MockTest.cs N nmock/test/NMock/PredicatesTest.cs N nmock/test/NMock/Dynamic/ClassGeneratorTest.cs No conflicts created by this import ***** Bogus filespec: - Imported sources |
From: Steve F. <sm...@us...> - 2002-10-06 14:18:42
|
Update of /cvsroot/mockobjects/no-stone-unturned/doc/xdocs In directory usw-pr-cvs1:/tmp/cvs-serv31067/doc/xdocs Modified Files: how_mocks_happened.xml _template.xml glossary.xml a_longer_example.xml patterns.xml Log Message: further work on how_mocks_happened Index: how_mocks_happened.xml =================================================================== RCS file: /cvsroot/mockobjects/no-stone-unturned/doc/xdocs/how_mocks_happened.xml,v retrieving revision 1.17 retrieving revision 1.18 diff -u -r1.17 -r1.18 --- how_mocks_happened.xml 5 Oct 2002 23:35:56 -0000 1.17 +++ how_mocks_happened.xml 6 Oct 2002 14:18:37 -0000 1.18 @@ -5,20 +5,22 @@ <title>Introduction</title> <para> - Mock Objects is a development technique that lets you unit test classes that you didn't think - you could <emphasis>and</emphasis> helps you write better code while doing so. This chapter - works through a simple example to show a compressed history of how Mock Objects was discovered by - refactoring from conventional unit tests and why they're useful. The chapter is unusual in that it's - focussed on the tests, it ignores the production code almost completely. - I'll be covering the whole cycle in later chapters but, for now, I want to concentrate - on the thought processes behind our approach to &TDD;. + Mock Objects is a development technique based on experience. It was discovered and refined amongst + the Extreme Programming community in London as we taught ourselves &TDD;, because we had people who + would ask the right pointed question and people who could make ideas work. We've stuck with + Mock Objects more than most teams because of the benefits we see, such as better code structure, + understandable unit tests, and testing against complex infrastrcture. I'll justify all of these + claims later in the book but, for now, I want to describe how we got there by refactoring + from conventional unit tests and why. This makes for an unusual chapter that's all tests and no + production code. </para> <para> - The concept of Mock Objects is based on two essential techniques: isolate the code under test by + The concept of Mock Objects is based on two techniques: isolate the code under test by emulating everything it interacts with and, put the test assertions <emphasis>inside</emphasis> those stub implementations so that you can validate the interactions between objects as they occur. - This sounds simple, but it has some powerful effects on how the code comes out. + This sounds simple — it <emphasis>is</emphasis> simple — but it has some powerful effects + on how we write software. </para> </section> <!-- Introduction --> @@ -58,7 +60,7 @@ </para> <programlisting> -public void testGotoSamePlace() { +public void testDoNotMove() { final Position POSITION = new Position(0, 0); Robot robot = new Robot(); @@ -89,17 +91,12 @@ assertEquals("Should be same move", new MoveRequest(1, MoveRequest.SOUTH), moves.get(1)); }</programlisting> - <note> - <para> - You may have noticed that we haven't said anything about the layout of the warehouse. We'll - assume for now that the Robot knows about it by some other mechanism. - </para> - </note> - <para> This test specifies two things: a simple routing scheme for moves to adjacent squares, and that we're - describing each step of the route with a <classname>MoveRequest</classname> object. We carry on and - pretty soon we have tests for moving the robot all over the building, for example: + describing each step of the route with a <classname>MoveRequest</classname> object. You may have noticed + that we haven't said anything about the layout of the warehouse, we'll assume for now that the Robot + knows about it by some other mechanism. We carry on and pretty soon we have tests for moving the robot + all over the building, for example: </para> <programlisting> @@ -169,7 +166,7 @@ Is there a better way? Can we find a technique that's less intrusive, that doesn't require the robot to hang on to unnecessary values? Can we have more helpful test failures that will fail faster, like we know we're supposed to? What would happen if we really believed in the object maxim - <glossterm linkend="telldontask"><quote>Tell, don't ask</quote></glossterm>? That is, what if we make a point + <glossterm linkend="telldontask"><quote>Tell, don't ask</quote></glossterm>? What if we make a point of avoiding getters? <footnote><para> John Nolan is credited with asking this question at the right time at a software architecture @@ -182,7 +179,8 @@ <title>Breaking apart the Robot</title> <para> - Let's stop and think for a moment. Our robot is actually doing two things when we ask it to move + Let's stop and think for a moment. Which object within a robot can tell that the correct route has been + generated? Our robot is actually doing two things when we ask it to move through the warehouse; it has to choose a route from its current position to its destination, <emphasis>and</emphasis> it has to move along the route it has chosen. Those activities are distinct, even if they might happen at the same time. If we break out these responsibilities into two @@ -227,7 +225,7 @@ </para> <programlisting> -public void testGotoSamePlace() { +public void testDoNotMove() { final Position POSITION = new Position(1, 1); <emphasis>Motor mockMotor = new Motor() { @@ -262,9 +260,9 @@ <screen> There was 1 failure: -1) testGotoSamePlace(test.nostone.RobotTest)junit.framework.AssertionFailedError: +1) testDoNotMove(test.nostone.RobotTest)junit.framework.AssertionFailedError: Should be no moves - at test.nostone.RobotTest.testGotoSamePlace(test.nostone.RobotTest); + at test.nostone.RobotTest.testDoNotMove(test.nostone.RobotTest); at tdd.nostone.RobotTest$Motor.move() at tdd.nostone.Robot.requestNextMove() at tdd.nostone.Robot.chooseBestPath() @@ -545,7 +543,7 @@ }); } - public void testGotoSamePlace() { + public void testDoNotMove() { final Position POSITION = new Position(1, 1); moveAndVerifyRobot(POSITION, POSITION); } @@ -755,18 +753,41 @@ holds together the objects that do the real work. This is how the process often works: we start with a top-level object, fill in some implementation, and hollow it out again as we understand more about what's inside. We then do the same to the new objects we've created, and so on. We end up with a collection of - classes, each focussed on a particular task, that have well-defined (and tested!) interfaces to their collaborating - objects. + classes, each focussed on a particular task, that have well-defined (and tested!) interfaces + to their collaborating objects. </para> + <para> + One more design issue is our unusually heavy use of the <quote>Tell, don't ask</quote> pattern. + We prefer to pass behaviour into an object, rather than pulling values out, which is why we wrote + a <quote>route following object</quote> rather than returning a collection of + <classname>MoveRequest</classname>. + My experience is that the common use of getters, particularly for collections, makes a class and + the code that uses it that little bit harder to change. Across a whole code base, the increased + <quote>refactoring drag</quote> can be significant. If it's not clear why getting collections can slow + things down, think of all the times you write iterator loops such as: + </para> + <programlisting> + Iterator moveRequests = robot.getMoveRequests(); + while (moveRequests.hasNext()) { + MoveRequest moveRequest = (MoveRequest)moveRequests.next(); + <lineannotation>// process moveRequest</lineannotation> + }</programlisting> + <para> + If we change to passing a <classname>Motor</classname> to a <classname>Robot</classname> which, in turn, + passes each step on the route to the <classname>Motor</classname>, then we've refactored the loop navigation; + it's done exactly once in the <classname>Robot</classname>. We've also made test failures more + accurate. Some of us think that this style is more expressive because it leaves the decision about + route management where it belongs, with the <classname>Robot</classname>, rather than the caller. + </para> + <sidebar> <title>Top-Down Decomposition</title> <para> Some of you may recognise the similarities between &TDD; and the ancient discipline of - <glossterm linkend="topdowndecomposition"><quote>Top-Down Decomposition</quote></glossterm>, also known as - <quote>Structured Programming</quote>. The idea is to start from the top-most level of the program, stub out - the bits that have not yet been implemented, and work your way down; repeat until you have a complete - application. In the days when many programmers found procedures and nested scope exotic, - Top-Down Decomposition was a useful technique for avoiding + <quote>Top-Down Decomposition</quote>, also known as <quote>Structured Programming</quote>. The idea + is to start from the top-most level of the program, stub out the bits that have not yet been implemented, + and work your way down; repeat until you have a complete application. In the days when many programmers + found procedures and nested scope exotic, Top-Down Decomposition was a useful technique for avoiding <glossterm linkend="sdd">Spaghetti-Driven Development</glossterm>. </para> <para> @@ -775,78 +796,33 @@ a couple of critical failings: it turned out to be hard to change early design decisions because, of course, they're embedded in the top-level structure, and it's not good at encouraging reuse between lower-level components. It looks like &TDD; avoids these problems because - object-orientation makes it easier to share components throughout the application and - the emphasis on refactoring means we can remove duplication as the codebase grows. + the combination of object-orientation and refactoring makes it easier to make radical changes to + the code, and to remove duplication by sharing components. </para> </sidebar> <!-- Top-Down Decomposition --> - <para> - One more design issue is our unusually heavy use of the <quote>Tell, don't ask</quote> pattern. - We prefer to pass behaviour into an object, rather than pulling values out, which is why we wrote - a <quote>route following object</quote> rather than returning a collection of - <classname>MoveRequest</classname>. - My experience is that the common use of getters, particularly for collections, makes a class and - the code that uses it that little bit harder to change. Across a whole code base, the increased - <quote>refactoring drag</quote> can be significant. If it's not clear why getting collections can slow - things down, think of all the times you write iterator loops such as: - </para> - <programlisting> -Iterator moveRequests = robot.getMoveRequests(); -while (moveRequests.hasNext()) { - MoveRequest moveRequest = (MoveRequest)moveRequests.next(); - <lineannotation>// process moveRequest</lineannotation> -}</programlisting> - <para> - If we change to passing a <classname>Motor</classname> to a <classname>Robot</classname> which, in turn, - passes each step on the route to the <classname>Motor</classname>, then we've refactored the loop navigation; - it's done exactly once in the <classname>Robot</classname>. We've also made test failures more - accurate. Some of us think that this style is more expressive because it leaves the decision about - route management where it belongs, with the <classname>Robot</classname>, rather than the caller. - </para> </section> <!-- What about design? --> <section> <title>What does this mean?</title> <para> - Mock Objects is a technique + At one level, Mock Objects are just stubs with assertions, but using them consistently within &TDD; + changes the way I write code (for the better, I think). It gives me a framework for identifying + objects and describing how they talk to each other; deciding what to verify forces me to clarify the + relationships between an object and its collaborators. Mock Objects makes the tests easier to understand + because each test exercises just one feature in an object. In turn, that makes refactoring easier + because there are fewer test side-effects when I change the code. The overhead of writing Mock Objects + may look high from this example but it's not like that in practice. Modern development environments will do + a lot of the work and a project, as it grows, will reach a critical mass of test infrastructure so that + new tests don't always require new Mock Objects — the benefits of refactoring apply as much to the + tests as to the production code. </para> -<para>Tests based on Mock Objects usually conform to a pattern: setup any -state, set expectations for the test, run the target code, and verify -that your expectations have been met. This style makes tests easy to -work with because they look similar and because all the interactions -with an object are local to a test fixture; I have found myself -contributing usefully to someone else's code base after only a few -minutes with the tests. More importantly, however, I constantly find -that the process of deciding what to verify in a test drives me to -clarify the relationships between an object and its collaborators. -The flex points I add to my code to provide support for testing turn -out to be the flex points I need as the use of the code evolves. -</para> -</section> <!-- What does this mean? --> - - <section> - <title>Conclusions</title> - - <para> - This simple example shows how refactoring tests with some design principles in mind led to the discovery of - an unusually fruitful development technique. Using Mock Objects in practice is slightly different. First, - the process of writing a test usually involves defining which mock objects are involved, rather than - extracting them afterwards. Second, in Java at least, there are the beginnings of a Mock Object library - for common classes and APIs. Third, there are now several tools and libraries to help with the construction - of Mock Objects. In particular, the <ulink url="http://www.mockobjects.com">www.mockobjects.com</ulink> site - includes a library of expectation objects. - </para> - <para> - The rest of this book will show you how to use Mock Objects and Test Driven Design in your development process. - We will work through some real-world examples to show how Mock Objects can be used to test Java APIs, drive - refactoring and eliminate dependencies on external components. And along the way we will annotate our examples - with tips and warnings to help you improve your technique and avoid pitfalls. - </para> <para> - Mock Objects and Test Driven Design have changed the way we develop code, and changed it noticeably for the - better. We hope that you can benefit from these techniques as well and that this book helps you to do so. + The critical insight of &TDD; is that tests are a design tool, not just for testing; the flex points I + add to make my code testable turn out to be the flex points I need as the use of the system evolves. + The critical insight of Mock Objects is that stubs are a design tool as well. </para> - </section> <!-- Conclusions --> + </section> <!-- What does this mean? --> </chapter> Index: _template.xml =================================================================== RCS file: /cvsroot/mockobjects/no-stone-unturned/doc/xdocs/_template.xml,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- _template.xml 12 Aug 2002 23:08:53 -0000 1.4 +++ _template.xml 6 Oct 2002 14:18:37 -0000 1.5 @@ -12,15 +12,21 @@ ]> <article> + <articleinfo> + <author> + <firstname>Steve</firstname> <surname>Freeman</surname> + <email>st...@m3...</email> + </author> + <copyright> + <year>@year@</year> + <holder>Steve Freeman</holder> + </copyright> + </articleinfo> + &@fragment.name@; <!-- need these to avoid dangling references in the fragment --> - <section> - <title>Patterns</title> - &patterns; - </section> - + &patterns; &glossary; -</article> - +</article> \ No newline at end of file Index: glossary.xml =================================================================== RCS file: /cvsroot/mockobjects/no-stone-unturned/doc/xdocs/glossary.xml,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- glossary.xml 20 Aug 2002 16:56:57 -0000 1.4 +++ glossary.xml 6 Oct 2002 14:18:37 -0000 1.5 @@ -44,14 +44,6 @@ </glossdiv> <!-- S --> <glossdiv> - <title>T</title> - - <glossentry id="topdowndecomposition"> - <glossterm>Top-Down Decomposition</glossterm> - </glossentry> - </glossdiv> <!-- T --> - - <glossdiv> <title>W</title> <glossentry id="wiki"> Index: a_longer_example.xml =================================================================== RCS file: /cvsroot/mockobjects/no-stone-unturned/doc/xdocs/a_longer_example.xml,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- a_longer_example.xml 8 Sep 2002 15:18:57 -0000 1.5 +++ a_longer_example.xml 6 Oct 2002 14:18:37 -0000 1.6 @@ -332,7 +332,7 @@ </section> <!-- We write our first implementation of the servlet --> <section> - <title>We accept a name and return a result from a hard-coded collection.</title> + <title>Handle no result and missing request name.</title> <para> Decided to return a single value for a known name. @@ -514,8 +514,111 @@ A useful clue is that we're not using "surname", the value of the name parameter. So, the test is driving us to add the book logic to the servlet. </para> - </section> <!-- accept a name and return a result from a hard-coded collection --> + <para> + First thing, rename testNoEntries to testMissingName and change expected return string. + </para> + + <programlisting> + assertEquals("Should be error message", "No name", page.toString().trim()); + </programlisting> + + <para> + Let's finish refactoring test case. Both tests use near identical MockRequest, but change on + returned parameter. Use a String instance variable to pass found name to MockRequest. + </para> + + <programlisting> + private String requestParameter; + private final ExpectationValue requestParameterName = new ExpectationValue("Request parameter name"); + private AddressBookServlet servlet = new AddressBookServlet(); + private HttpServletRequest mockRequest = new NullHttpServletRequest() { + public String getMethod() { + return "GET"; + } + + public String getProtocol() { + return "HTTP/1.1"; + } + + public String getParameter(String parameterName) { + requestParameterName.setActual(parameterName); + return requestParameter; + } + }; + + public void testMissingName() throws ServletException, IOException { + requestParameterName.setExpected("name"); + + servlet.service(mockRequest, mockResponse); + + requestParameterName.verify(); + assertEquals("Should be error message", "No name", page.toString().trim()); + } + + public void testFindOneAddress() throws ServletException, IOException { + requestParameterName.setExpected("name"); + + requestParameter = "surname"; + servlet.service(mockRequest, mockResponse); + + requestParameterName.verify(); + assertEquals("Should be expected address", "surname@domain", page.toString().trim()); + }</programlisting> + + <comment> + Things have been a bit misguided so far. We should have been testing for the name parameter from + the start as that's in the requirements. Need to rework this section to get to this point. Now + </comment> + </section> <!-- Handle no result and missing request name --> + + <section> + <title>Now we return a found address for a hard-coded name</title> + + <para> + Add a test for a found address. + </para> + + <programlisting> + public void testFindOneAddress() throws ServletException, IOException { + requestParameterName.setExpected("name"); + + requestParameter = "somebody"; + servlet.service(mockRequest, mockResponse); + + requestParameterName.verify(); + assertEquals("Should be expected address", "somebody@domain", page.toString().trim()); + }</programlisting> + + + <para> + Now we have two tests based on the requested name that return different results. How are we going to + tell the servlet? First, just hard-code it. We'll pull the implementation into a helper method to + make it more readable. (Yes, it's disgusting). + </para> + + <programlisting> + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String name = request.getParameter("name"); + + response.setContentType("text/plain"); + response.getWriter().println( + lookupName(name)); + } + + private String lookupName(String name) { + if (name == null) { + return "No name"; + } else if (name.equals("somebody")) { + return "somebody@domain"; + } else { + return "No address found"; + } + }</programlisting> + + + </section> <!-- Now we return a found address for a hard-coded name --> <!-- TODO --> <section> Index: patterns.xml =================================================================== RCS file: /cvsroot/mockobjects/no-stone-unturned/doc/xdocs/patterns.xml,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- patterns.xml 18 Aug 2002 22:44:39 -0000 1.3 +++ patterns.xml 6 Oct 2002 14:18:37 -0000 1.4 @@ -1,4 +1,5 @@ <glossary status="draft"> + <title>Patterns</title> <glossdiv> <title>A</title> |
From: Steve F. <sm...@us...> - 2002-10-05 23:35:58
|
Update of /cvsroot/mockobjects/no-stone-unturned/doc/xdocs In directory usw-pr-cvs1:/tmp/cvs-serv9684/doc/xdocs Modified Files: how_mocks_happened.xml Log Message: reworking Index: how_mocks_happened.xml =================================================================== RCS file: /cvsroot/mockobjects/no-stone-unturned/doc/xdocs/how_mocks_happened.xml,v retrieving revision 1.16 retrieving revision 1.17 diff -u -r1.16 -r1.17 --- how_mocks_happened.xml 2 Sep 2002 20:13:29 -0000 1.16 +++ how_mocks_happened.xml 5 Oct 2002 23:35:56 -0000 1.17 @@ -8,22 +8,17 @@ Mock Objects is a development technique that lets you unit test classes that you didn't think you could <emphasis>and</emphasis> helps you write better code while doing so. This chapter works through a simple example to show a compressed history of how Mock Objects was discovered by - refactoring from conventional unit tests and why they're useful. - </para> - - <para> - Mock Objects is based on two key concepts: replace everything except the code under test with - mock implementations that emulate the rest of the environement, and put the test assertions - <emphasis>inside</emphasis> those mock implementations so that you can validate the interactions - between the objects in your test case as they occur. + refactoring from conventional unit tests and why they're useful. The chapter is unusual in that it's + focussed on the tests, it ignores the production code almost completely. + I'll be covering the whole cycle in later chapters but, for now, I want to concentrate + on the thought processes behind our approach to &TDD;. </para> <para> - This chapter is unusual in that it's focussed on the tests, it ignores the production code almost completely. - I'll be covering the whole cycle in later chapters but, for now, I want to concentrate - on the thought processes behind a particular approach to &TDD;. This chapter is not about - how to develop routing software for a robot, but how to structure your code so that you can tell if such a - module works. + The concept of Mock Objects is based on two essential techniques: isolate the code under test by + emulating everything it interacts with and, put the test assertions <emphasis>inside</emphasis> + those stub implementations so that you can validate the interactions between objects as they occur. + This sounds simple, but it has some powerful effects on how the code comes out. </para> </section> <!-- Introduction --> @@ -102,7 +97,7 @@ </note> <para> - This test specifies a simple routing scheme, for moves to adjacent squares, and that we're + This test specifies two things: a simple routing scheme for moves to adjacent squares, and that we're describing each step of the route with a <classname>MoveRequest</classname> object. We carry on and pretty soon we have tests for moving the robot all over the building, for example: </para> @@ -148,7 +143,7 @@ <para> We will have to re-run the code to find the problem because the assertions have been made <emphasis>after</emphasis> the call has finished. It could be worse, the robot is a relatively - simple example. Some of us tried to do this with financial mathematics and it was very painful when a + simple example. I was on a team that tried to do this with financial mathematics and it was very painful when a calculation failed. A test failure usually meant carefully stepping through the code with an open spreadsheet nearby to check the intermediate values. </para> @@ -158,8 +153,9 @@ to hold all the <classname>MoveRequest</classname>s since the last <methodname>goTo()</methodname>. We don't have any other immediate need for <methodname>getRecentMovesRequests()</methodname> and, what's worse, we've made an implicit promise to other people who work with the <classname>Robot</classname> - code that we will store routes, thus shrinking (ever so slightly) our room to manouver. We can mark the method as - test infrastructure, but this sort of notation is clumsy and is often ignored in the heat of development. + code that we will store routes, thus shrinking (ever so slightly) our room to manouver. We could mark the + method as test infrastructure, but this sort of notation is clumsy and is often ignored in the heat + of development. </para> <para> @@ -247,14 +243,13 @@ assertEquals("Should be same", POSITION, robot.getCurrentPosition()); }</programlisting> - <note> - <para> - I'm being inconsistent again by keeping <function>getCurrentPosition()</function>. In my defence, - it seems more reasonable to me that a robot should know its current position than that it should - keep a list of its recent movements. One day I'll replace this with a - <classname>CurrentPositionListener</classname>, or some such technique. Really I will. - </para> - </note> + <para>Now we have a little object cluster that looks like this:</para> + + <informalfigure> + <mediaobject><imageobject> + <imagedata fileref="figures/how_mocks_happened_1.gif" align="center"/> + </imageobject></mediaobject> + </informalfigure> <para> This version of the test still describes the same core specification as before (the @@ -280,10 +275,22 @@ <para> which gives a much clearer view of where the error became visible. If finding the problem turns out to be harder, we can trap the &junit; <classname>AssertionFailedError</classname> exception in the development - tool to bring up the debugger. Then we can explore the program state at the time of the failure, without + tool to bring up the debugger; then we can explore the program state at the time of the failure, without having to step through from the beginning of the test. This technique doesn't work all the time but, more often than not, it takes you to straight to the source of the problem. </para> + + <note> + <para> + I'm being inconsistent again by keeping + <function>getCurrentPosition()</function>. In my defence, + it seems more reasonable to me that a robot should know its current position than that it should + keep a list of its recent movements. One day I'll replace this with a + + <classname>CurrentPositionListener</classname>, or some such technique. Really I will. + + </para> + </note> </section> <!-- Breaking apart the Robot --> <section> @@ -317,29 +324,29 @@ <para> The first assertion will fail if the <classname>Robot</classname> sends the wrong - <classname>MoveRequest</classname>. The second assertion will fail if the - <classname>Robot</classname> calls <function>move()</function> more than once. This test won't - fail if the <classname>Robot</classname> doesn't call <function>move()</function> at all, as it won't - try either assertion. We can only tell whether this has happened after - <function>goTo()</function> has finished, so we need to record how many times <function>move()</function> - was called. Unfortunately, we can't just push <varname>moveCount</varname> up from the anonymous - <classname>Motor</classname> class to the test method because a quirk of Java requires such variables to - be declared <token>final</token> and we can't change the value of a <type>final int</type>. I can think of - two alternatives that would solve our immediate problem. + <classname>MoveRequest</classname> and the second assertion will fail if the + <classname>Robot</classname> calls <function>move()</function> more than once. This test, however, won't + fail if the <classname>Robot</classname> forgets to call <function>move()</function> altogether because + then it won't try any of the assertions. We can only find missing calls after <function>goTo()</function> + has finished, so we need to record how many times <function>move()</function> is called. Unfortunately, we + can't just push <varname>moveCount</varname> up from the anonymous <classname>Motor</classname> class to the + test method because Java requires such variables to be declared <token>final</token> and we can't + change the value of a <type>final int</type>. I can think of two alternatives that would solve + our immediate problem. </para> <para> First, we could write a named implementation of <classname>Motor</classname> so that the test can see the <varname>moveCount</varname> field. </para> <programlisting> -<emphasis>static class AbstractMotorStub implements Motor { +<emphasis>static class CountingMotorStub implements Motor { int moveCount = 0; }</emphasis> public void testMoveOneStep() { final Position DESTINATION = new Position(1, 0); - AbstractMotorStub mockMotor = new AbstractMotorStub() { + CountingMotorStub mockMotor = new CountingMotorStub() { public void move(MoveRequest request) { assertEquals("Should be move", new MoveRequest(1, MoveRequest.SOUTH), request); moveCount++; @@ -385,7 +392,8 @@ <para> Personally, I prefer the first of these two options because I find it easier to see the boundaries - between the stub and the test classes. That said, there's not a huge difference, there are plenty of + between the stub and the test classes. It also sets me up better for pulling out a mock implementation that + I can share between tests. That said, there's not a huge difference, there are plenty of people making effective use of Self Shunt, and it's more important that your team is consistent about its approach. <tip> @@ -421,7 +429,7 @@ <para> We can use the <varname>expectedMoveRequests</varname> in the mock motor. Every time an actual - move arrives from the robot, we pull out the next value from the list of expected moves and compare the + move arrives from the robot, we pop the next value from the list of expected moves and compare the two. If there's a mismatch then our routing software has gone wrong and the test fails. </para> @@ -491,7 +499,7 @@ original <quote>playback</quote> version that recorded the most recent movements. The answer is that, by moving the collection from the production code into the test infrastructure, we've made our design testable without adding functionality that the system doesn't need. We've kept our options a little bit more open and, - as a side benefit, we have much more useful failure messages. + as a side benefit, we have more useful failure messages. </para> </section> <!-- Testing more than one step --> @@ -602,13 +610,12 @@ <tip> <title>Go to Lourdes</title> <para> - Throw away your software crutches occasionally to test your assumptions just one more time; - this is one of the movitivations for the XP rule - <glossterm linkend="dtsttcpw"><quote>Do The Simplest Thing That Could Possibly Work</quote></glossterm>. - I'm always surprised how much better I can do something the next time around after I have more - experience. For example, I've found that writing the examples for this book a real eye-opener - because I've had to work out some of my practices from first principals again. - (Lourdes is a shrine in France famous for healing the lame, the walls are covered in discarded crutches.) + I've found writing the examples for this book a real eye-opener because I've had to work out + some of my practices from first principals again. Sometimes it's a good idea to throw away your + software crutches occasionally to test your assumptions just one more time, which is one of the + motivations for the XP rule <glossterm linkend="dtsttcpw"><quote>Do The Simplest Thing That + Could Possibly Work</quote></glossterm>. (Lourdes is a shrine in France famous for healing the + lame, the walls are covered in discarded crutches.) </para> </tip> </para> @@ -645,7 +652,7 @@ }</programlisting> <para> - Starting a new class costs a little extra, but it does bring some benefits. + Converting to a named class costs a little extra, but it does bring some benefits. First, it makes the tests a bit more legible because it moves the behaviour (managing lists of move requests) to the object that should know about it (the <classname>Motor</classname>). If we rework one of our tests, it comes out as: @@ -692,10 +699,10 @@ <tip> <title>Steady promotion</title> <para> - Often, I evolve a mock class by starting with an anonymous implementation in the test class, + Nowadays, I often evolve a mock class by starting with an anonymous implementation in the test class and then extracting a separate mock class if the code becomes anything more than trivial. The mock - class may start as an inner class of the test case (because my development environment makes - that refactoring easy), which I'll promote to package level if I need it elsewhere. + class may start as an inner class of the test case, because my development environment makes + that refactoring easy, which I'll promote to package level if I need it elsewhere. </para> </tip> @@ -717,12 +724,11 @@ decided that we needed a <quote>route following object</quote>, a <classname>Motor</classname>, so that we could tell whether the route has been properly constructed; we invented a construct in the software to represent something - in the physical world. This sounds to me like Object-Oriented Design (or at least some of it), - and we have found a way to drive that decision from an immediate requirement in a test. - A motor is straightforward, in later examples I'll show how we can be more sophisticated and - invent objects to represent computatational concepts such as callbacks and listeners. - </para> - <para> + in the physical world. This sounds to me like Design, and we have driven that + decision from an immediate requirement in a test. + A motor is a straightforward concept, in later examples I'll show how we can be more + sophisticated and invent objects to represent computatational concepts such as callbacks + and listeners. The next decision was how to connect the motor object to the robot object, so that it can move. We could have passed the <classname>Motor</classname> through with the <function>goTo()</function> method, but instead we decided to make the <classname>Motor</classname> @@ -731,25 +737,25 @@ </para> <para> We're designing each unit of code from the outside-in, from how the caller sees it. When we - program, writing tests with Mock Objects makes us think about the objects it - depends on, its environment. Sometimes those objects don't yet exist, which means that we've + program, writing tests with Mock Objects makes us think about the other objects that the target code + depends on, its environment. Sometimes those objects don't exist yet, which means that we've found a conceptual <quote>fault line</quote> in the system that we can turn into - a new object interface. In our tests, the expectations describe what the objects should say to each other, + a new interface. In our tests, the expectations describe what the objects should say to each other, their <emphasis>protocol</emphasis>, without having to figure out how the new objects actually work. We fake them up for now and move on to implementing them when we're ready. Applied consistently, this pushes us towards a codebase made up of components with clear interfaces which (at least in my experience) makes good things like reusability happen almost as a byproduct. What makes &TDD; so powerful is that we can do this incrementally, evolving the code from the requirements in hand rather than - trying to place bets on the what we think we're going to need. + having to place bets on the what we think we will need. </para> <para> - Speculating wildly, the next requirements might push us to extract a <classname>Navigator</classname> - object that works out the route, so that the <classname>Robot</classname> becomes just a frame that + Speculating wildly, the next requirements in this example might push us to extract a <classname>Navigator</classname> + object that works out the route, so that eventually the <classname>Robot</classname> becomes just a frame that holds together the objects that do the real work. This is how the process often works: we start with a - top-level object, fill in some implementation, and hollow it out as we understand more about what's + top-level object, fill in some implementation, and hollow it out again as we understand more about what's inside. We then do the same to the new objects we've created, and so on. We end up with a collection of - classes, each focussed on a particular task, that have well-defined interfaces to their collaborating + classes, each focussed on a particular task, that have well-defined (and tested!) interfaces to their collaborating objects. </para> <sidebar> @@ -772,24 +778,19 @@ object-orientation makes it easier to share components throughout the application and the emphasis on refactoring means we can remove duplication as the codebase grows. </para> - </sidebar> + </sidebar> <!-- Top-Down Decomposition --> <para> One more design issue is our unusually heavy use of the <quote>Tell, don't ask</quote> pattern. We prefer to pass behaviour into an object, rather than pulling values out, which is why we wrote a <quote>route following object</quote> rather than returning a collection of <classname>MoveRequest</classname>. - Our experience is that the common use of getters, particularly for collections, makes a class and + My experience is that the common use of getters, particularly for collections, makes a class and the code that uses it that little bit harder to change. Across a whole code base, the increased - <quote>refactoring drag</quote> can be significant. - </para> - - <note> - <para> - If it's not clear why getting collections can slow things down, think of all the times - you write iterator loops such as: - </para> - <programlisting> + <quote>refactoring drag</quote> can be significant. If it's not clear why getting collections can slow + things down, think of all the times you write iterator loops such as: + </para> + <programlisting> Iterator moveRequests = robot.getMoveRequests(); while (moveRequests.hasNext()) { MoveRequest moveRequest = (MoveRequest)moveRequests.next(); @@ -797,74 +798,19 @@ }</programlisting> <para> If we change to passing a <classname>Motor</classname> to a <classname>Robot</classname> which, in turn, - passes each step on the route to the <classname>Motor</classname>, then we've refactored the loop navigation. - At the very least, we've made the tests more accurate. If we need to iterate over this collection in more - than one place, then we've also removed some duplication. Some of us think that this style is also more - expressive because it leaves decisions about route management with the <classname>Robot</classname>, rather - than the caller. + passes each step on the route to the <classname>Motor</classname>, then we've refactored the loop navigation; + it's done exactly once in the <classname>Robot</classname>. We've also made test failures more + accurate. Some of us think that this style is more expressive because it leaves the decision about + route management where it belongs, with the <classname>Robot</classname>, rather than the caller. </para> - </note> - - </section> <!-- What about design? --> - <!-- TODO --> <comment>Under development</comment> - <para> - Design by composition, top-down with polymorphism, do the protocols first, shows the fault lines but - it's more than divide and conquer. - </para> - - <section> <title>What does this mean?</title> -<para>My code moved in this direction because I was committed to unit -testing but didn't want to record and expose unnecessary details about the -state of my Robot -(the <function>getRecentMoveRequests()</function> method). -As a result, I have better unit tests and a cleaner interface to the Robot -class. But even better than that, by following the needs of my tests I have -actually ended up improving the object model of the Robot by separating the -Robot from its Motor and defining the an abstract interface between the two. -I now have the flexibility to subtitute a different Motor implementation, -perhaps one that accelerates. Similarly, if I want to track the total -distance a Robot travels, I can do so without changing the implementation -of either the robot or its motor:</para> - -<programlisting> -/** A decorator that accumulates distances, then passes the request - * on to a real Motor. - */ -public class MotorTracker implements Motor { - private Motor realMotor; - private long totalDistance = 0; - - public MotorTracker(Motor aRealMotor) { - realMotor = aRealMotor; - } - public void move(MoveRequest request) { - totalDistance += request.getDistance(); - realMotor.move(request); - } - public long getTotalDistance() { - return totalDistance; - } -} - -// When constructing the Robot, wrap the implementation of a -// Motor that does the work in a MotorTracker. -OneSpeedMotor realMotor = new OneSpeedMotor(); -MotorTracker motorTracker = new MotorTracker(realMotor); -Robot = new Robot(motorTracker); -// do some travelling here &elipsis; -println("Total distance was: " + motorTracker.getTotalDistance()); -</programlisting> - -<para>Neither changing the Motor implementation nor adding tracking -functionality would have been so easy if I had stuck with a testing -strategy based on stashing the intermediate route. The next step might -be to introduce a <classname>Navigator</classname> object to work out -the route, and the Robot would link the two together.</para> + <para> + Mock Objects is a technique + </para> <para>Tests based on Mock Objects usually conform to a pattern: setup any state, set expectations for the test, run the target code, and verify |
From: Steve F. <sm...@us...> - 2002-10-05 23:35:24
|
Update of /cvsroot/mockobjects/no-stone-unturned/doc/xdocs In directory usw-pr-cvs1:/tmp/cvs-serv9523/doc/xdocs Modified Files: htmlbook.css Log Message: further style tweaks Index: htmlbook.css =================================================================== RCS file: /cvsroot/mockobjects/no-stone-unturned/doc/xdocs/htmlbook.css,v retrieving revision 1.15 retrieving revision 1.16 diff -u -r1.15 -r1.16 --- htmlbook.css 2 Sep 2002 22:39:25 -0000 1.15 +++ htmlbook.css 5 Oct 2002 23:35:22 -0000 1.16 @@ -15,14 +15,14 @@ font-weight: normal; font-size: 140%; } DIV.section H3 { font-style: italic; font-weight: normal; } -.tip { border: solid black 1px; } +.tip { border-top: solid black 1px; border-bottom: solid black 1px; } DIV.tip H3 { font-size: 80%; font-style: italic; font-weight: normal; font-size: small; } DIV.tip P { font-size: 80%; } -.note { border: solid black 1px;} -DIV.note { margin-left: 10%; }; +.note { border-top: solid black 1px; border-bottom: solid black 1px;} +DIV.note { margin-left: 10%; margin-right: 10% }; DIV.note TH { font-size: 80%; } -DIV.note P { font-size: 80%; } +DIV.note P { font-size: 80%; align: center; } .sidebar { border-top: solid black 1px; border-bottom: solid black 1px; font-family: Arial, Helvetica, sans-serif; font-size: 80%; |
From: Steve F. <sm...@us...> - 2002-10-05 23:35:08
|
Update of /cvsroot/mockobjects/no-stone-unturned In directory usw-pr-cvs1:/tmp/cvs-serv9413 Modified Files: build.xml Log Message: added UML figures Index: build.xml =================================================================== RCS file: /cvsroot/mockobjects/no-stone-unturned/build.xml,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- build.xml 17 Aug 2002 13:20:07 -0000 1.6 +++ build.xml 5 Oct 2002 23:35:06 -0000 1.7 @@ -87,18 +87,24 @@ </target> <target name="CopyOtherFiles" - depends="CallMeFirst"> + depends="CallMeFirst"> <mkdir dir="${out.doc.dir}"/> - <mkdir dir="${out.doc.dir}/images"/> <!-- Copy the images --> + <mkdir dir="${out.doc.dir}/images"/> <copy todir="${out.doc.dir}/images"> <fileset dir="${xdoc.dir}/images"/> </copy> - <copy todir="${out.doc.dir}/images/style"> - <fileset dir="${xsl.dir}/images"/> - </copy> + <!-- Copy the figures --> + <mkdir dir="${out.doc.dir}/figures"/> + <copy todir="${out.doc.dir}/figures"> + <fileset dir="${xdoc.dir}/figures"/> + </copy> + + <copy todir="${out.doc.dir}/images/style"> + <fileset dir="${xsl.dir}/images"/> + </copy> <!-- Copy other files (non XML) --> <copy todir="${out.doc.dir}"> |
From: Steve F. <sm...@us...> - 2002-10-05 23:34:38
|
Update of /cvsroot/mockobjects/no-stone-unturned/doc/xdocs/figures In directory usw-pr-cvs1:/tmp/cvs-serv9147/doc/xdocs/figures Added Files: how_mocks_happened_1.gif Log Message: added UML figures --- NEW FILE: how_mocks_happened_1.gif --- GIF89a¯ 3¢ñL*Ì¦ó §¨õÍj·ÜdU ¹þhÆGå'¨¸FpÈ)¹Pø9©é·ùØi jzz' PÚÚV9:ÄêJ[Û0k»«Ë£»ü© ¼xTVlÚë«êL-<F¬Vª+-¸Ì£m¼×ö- 4éZ£WëüP'Ó]Ûv·@õmجÂå&8á¼Uè¾í³òÈ´¯z^iøóéÑ-'Ýúúâ(|W«:õé-¯Ò«Q{vílͳ:|øñcÓ>n*LìVþñ¯ænÂãÉ×Txô¡Û%ÐW1¡×{¥yö^nèµUQúµh p~¸#ôòæt`¶d~vf§ÜüIh¡2±¢g¬(y ä©h{FMA©hrfÏ¥ÝqZÖ¦ 2êi ²&*§¤úÅ©¥éª¬*çê[´Ö*ë¬ëÔú¬Qn§["«®M!VþZXU>8ÎRcPMd©±è6Ø´8eÑk·$ VzÅ`L ÷bQ#£ðïVkíòpÆGmø0¹s±»Za\£È(r÷:LòÂ?ÜmR=IgÅ0ÎôïÉ\2Q9ÿÃrKãfSã±¢sÑ`~´Ë#ÃLsÐ4GI±ÍMÊw¬!ÀIÍì XwôØJOÁ|"ì\2Èa¥¯®+ÔKXS%Ó¿ä>øÕºk'E/ÛîcoÛn,,¤ÃNhuزþóÌ£»ã[9ÇAªùÔ5ßêjݳÎ+®dç³{ìU!{íZ¸þºíºï(XõïÂÿ<Ä<eæ$Ï|óÅ_ã|ôÒs ýôÖ_OúòØoo=6Üß<ÒàO|6ä»ùè¯_q1Á³?\îÇO£VÔþúïÏÿþÿ@2½/ ±)CâxÁ#"J\¢Å(JqT¬"þ«hÅÿa1ýÛ"÷çÅ/æ/bïÎFÓ±J¼JÎE:\Ñq,Ý«XÇS©QyÕ©ØGUýq´ 0:²Âìñ Û!)¡Ñ]fRl$±K _ÊBÕGÐHAm$ëÊZxAÉ(rF3KÑÜÖ2a,«IXVʳÌb(T8]¶ n²L%I©^Ò±71æ)eânüäó¨Yk Dª §?Ôi` QB¦ð©QdQ«Ù¯ÂÓN®DÜPâ¢ÖªÉTûÕUõ`TÑÛ¾p&Ö£Á] ûêßR¦UêmÍf«+×Ö² µUlYÙúÚ¯¸LãYójX±îÌeIë8 |
From: Steve F. <sm...@us...> - 2002-10-05 23:34:38
|
Update of /cvsroot/mockobjects/no-stone-unturned/doc/argouml In directory usw-pr-cvs1:/tmp/cvs-serv9147/doc/argouml Added Files: how_mocks_happened.zargo Log Message: added UML figures --- NEW FILE: how_mocks_happened.zargo --- PK XB±q"{âS J,ôPÝIþè SdÜ:mÖ\@ í+a}ýlÓrPO {É©u¤Dnâ rÔüM²Gþ7,´JÁºüénG§oWõv«Ûu0ª¡áS{à ´û~~òi÷ÞÆ¿ñ*ÏoÙ¬+ನã°aFü{(²ùßl£×iߣÎEßpÅl¯ÆÜ2õq½YÈ(/¢{Í@ô"h"l¬êèµÑ[¦éKjµæi/ê¶°ô«fïÝÇY!ëÂ9ê É »1¶!ÞPQ&;TÝ2qéà°¥?È¢ø4$åà9£WÁ|þÎçñ·±ùý¿*r=y¿¦¼w8ópÀpÆî0BH:ipü>8ϬÏÌccù%^¯Aéc3=F¾¦(Ci.*$Í ä·|¦RDélUøÄùZÐ+ ¢°'ó]GIÚâ°¾ÌYD´ÈXæÏt@RÔ9¾lï@ßtáßCàI X>ZdD¾ÈmÓxuöµ®ÃƳúóÜ §ù¯Kôe2v¦Ü¼xVþiÃIÁç²^ï%kR;bÏ)oäDwÎ`ì È ³næüñkþ¡r:. ü½àøý°ÏÇ0,|ÙÐ]Êøò¤û-Oê£xè²oÅòQâÝ(ùÔL×[ ª7¦8X'.HòMª[F£=¸T×ç ·ÍßXóÞwUñà$8Ý3À²UAKó-ÙK=ë^Ôû5ËHP><õDWÜ"~ýàPúY'E?àÃN6ñ/Ö=ôkü¢{@'ýx0^%üÍ?PKn]~Z! Â0¼þÃûƦÕZÅêA=BõàIbó `[I¬xðãMª;ÌìÌÌïê/iiê0uÙSëlfÙxÀrÑïÍëÃêt9nà¡ý[q)N= $#¤+y<;ÓÆjÊnZ¯ +×W´uòZr'ikèy¿;;¹òhýc?L1#2!&²I³ni¢RóNR¤ßnj^ÉxOðØBaèå/PKÄú× uJ=E £ ¢êÞ\ Űd ©4Ò¶ì/C¾ ÅÄ &0ªYûyѸà2'UÙ Å&YÏ(òÖadôqÆå1â¨nyJë©L«®5îÐb,"ªg£ÓÀôÍæ<mÂÁåÒý Ò }À÷6ï¶ÌvS£ZÔ»³jGj¡9 7I@ÝÕ²Ww"X®>ɤnæKè£g §ð´u®*tAÞ³e¤ ¸0 (YÈCɦeYJX£5©âðsî(âXDµ¿Fþ!/¾ÊSsèªhø;[ªn(l¬7Ùêe«ë Ô*|ÓÔ=a }T¶2ÎÕW8µº®ÅS¥¸ZC¿Ìöº E=6R»H.W+Ù`w8Õ ø[\¿ß6ÞYôÖÉ,lÌc"ëí*/,î3(olúµþÀ8ÜÄá2uu/Okó¨YèÓ~.àµm²n°ÍI«G\G-ðPÔÜÁ\Éê)ã4®A~@kF¸û] ÜÔ:ªÄÖÁÔ!wý¤+c© ÎO¸Òõ¾Â«×çMýE¥X×Nmsè×ííJþ¿*ÈÊELçXÿZ?Üæõ#pýÔ§Ã97;ôä¶å⤣ðVðÏ+Nr ÕºóZ£FÐ'µ ¶8éðp÷¼ £xQa·ÕÉfï?îgr«¶Sî¶8y\ÞI¡}kµIÍÀ´ç?ägݸú«ÆüûÕPKÜÞ4Î |
From: Steve F. <sm...@us...> - 2002-10-05 23:34:05
|
Update of /cvsroot/mockobjects/no-stone-unturned/doc/xdocs/figures In directory usw-pr-cvs1:/tmp/cvs-serv8969/doc/xdocs/figures Log Message: Directory /cvsroot/mockobjects/no-stone-unturned/doc/xdocs/figures added to the repository |
From: Steve F. <sm...@us...> - 2002-10-05 23:33:48
|
Update of /cvsroot/mockobjects/no-stone-unturned/doc/argouml In directory usw-pr-cvs1:/tmp/cvs-serv8789/doc/argouml Log Message: Directory /cvsroot/mockobjects/no-stone-unturned/doc/argouml added to the repository |
From: Steve F. <st...@m3...> - 2002-10-03 12:01:16
|
Incidentally, you might want to look at using AspectJ to test this kind of stuff, then you could override the implementation of getPooledConnection() and return whatever you want. See http://www.xprogramming.com/xpmag/virtualMockObjects.htm for an example. S. ----- Original Message ----- From: "Simon Levitt" <sim...@uk...> To: "Steve Freeman" <st...@m3...> Cc: <moc...@li...> Sent: Friday, September 13, 2002 10:01 AM Subject: Re: [MO-java-dev] verify()ing object hierachies. > On Friday 13 September 2002 00:07, Steve Freeman wrote: > > I'd like to see some concrete examples, especially for ExpectationMap > > which is, itself a verifiable. It's sounds like some of your tests may > > be going too deep. You can overspecify, as well as underspecify. > > > Concrete example: > > We've got our own JDBC Connection Pool implementation which like most has > a finite set of actual JDBC Connections that it can allocate to threads. > > Each piece of code wanting to use JDBC talks directly to the Pool asking > for a connection to a particular named databaase (through a symbolic > name). > > Thus the call to the Connection Pooling classes is performed within the > method we want to test. > > e.g: > > public Person getPerson(String name) { > // PooledConnection is a wrapper round Connection > PooledConnection conn = ConnectionPool.getPooledConnection("persondb"); > > try { > conn.enable(); // Actually gets JDBC Connection from pool > stmt = conn.prepareStatement(...); > > results = stmt.exeuteQuery(); > person = new Person(results): > } > catch (SQLException e) { > conn.disable(); > } > reutrn person; > } > > I've developed a Mocked version of the Connection Pooling system to allow > these methods to be tested. > > What happens is the setup calls on MockConnectionPool populate an > ExpectationMap from pool name to MockPooledConnection - so you can test > the expectation that certain database connections will be/are being used. > verify() is checking expected keys vs actual keys. > > What I'd like to automate is the verification of the expectations set on > the MockPooledConnection objects contained in the ExpectationMap (ie. its > values). > > I think it would also be useful to check the MockStatements contained > therein, and their MockResultSets as well. > > You can obviously manually do this at the moment however verifying the > expectations means keeping a lot of object references locally to the test > method for the duration of the whole test. > > Simon., > -- > -------------------------------------------------------------------------- > Simon Levitt, Senior Development Engineer @ WorldPay plc, > WorldPay Centre, The Science Park, Milton Rd., Cambridge, CB4 0WE, ENGLAND > Sim...@uk... Ph:+44(0)1223 715151 F:+44(0)1223 715157 > ------------------------- http://www.worldpay.com/ ----------------------- > > > > ------------------------------------------------------- > This sf.net email is sponsored by:ThinkGeek > Welcome to geek heaven. > http://thinkgeek.com/sf > _______________________________________________ > Mockobjects-java-dev mailing list > Moc...@li... > https://lists.sourceforge.net/lists/listinfo/mockobjects-java-dev > |
From: Steve F. <st...@m3...> - 2002-10-03 11:33:25
|
From: "Simon Levitt" <sim...@uk...> > On Sunday 29 September 2002 17:58, Steve Freeman wrote: > > I'd like to propose that a Verifiable loop is a code smell which > > implies that something has become too complicated, so we shouldn't > > compensate for it. > > > > Thoughts? > > > As I introduced the code that tried to cope with smell (in a simple way - > through the static Vector) I suppose I should comment. > > I think you may be over generalising here in that MockObjects currently > only has one place where a child requires a reference to its parent > (which has a reference to its children), and you're trying to avoid the > potential problem by defining it as a smell. > > The aim of MockObjects, surely?, is to support anything that can be thrown > at it. Isn't just ignoring this kind of 'problem' limiting? The > MockObjects must be as complicated as the thing they are trying to Mock. > Ignoring parts of industry standard interfaces sounds like a death > sentence to me... Not exactly. We also use complexity in testing as a hint that our design might be getting out of hand. For example, what kind of code would get a statement from a connection, and then the connection from the statement, in the same fragment? Sounds like there are two pieces there that could be tested seperately. In that case, you should have different mock clusters set up for each piece and there should be no recursion. The problem then becomes one of making test setup lightweight and easy, rather than making the mock complete. We need to find ways for people to do everything they need to, but that doesn't mean that we need a single mock implementation for all possible tests. Increasingly, I find myself creating a null implementation of an interface and then overriding the methods I need for a particular test in an anonymous class. One of the essential points about testing with mocks is that the mocks must be simple enough that the failures will always be in the target code and that this should be obvious. Our mock implementations are now getting to the point where they need tests themselves and that bothers me. Hmmm. Perhaps we should be gathering patterns of use for each library and writing some canned setups for each one? > Having ideals is fine, sticking them to the end without compromise is quite often stupid. Easy now, in a moment one of us will use the N?zi word and the game will be over ;-) > Also in certain cirumstances Verifier can be involved in mantaining the > infinate loops and inherant stack overflow that occurs in these > circumstances, is this the sort of image a project that should be capable > of being used in the commerce commercial quality projects be projecting? Don't understand this. > As far as the JDBC problem goes could it be gotten around by storing the > parent references as the implemented interface rather than the Mock > reference? True, or a simple null stub. Depends on what the test needs. Steve |
From: Simon L. <sim...@uk...> - 2002-09-30 13:59:06
|
On Sunday 29 September 2002 17:58, Steve Freeman wrote: > I'd like to propose that a Verifiable loop is a code smell which > implies that something has become too complicated, so we shouldn't > compensate for it. > > Thoughts? > As I introduced the code that tried to cope with smell (in a simple way -=20 through the static Vector) I suppose I should comment. I think you may be over generalising here in that MockObjects currently=20 only has one place where a child requires a reference to its parent=20 (which has a reference to its children), and you're trying to avoid the=20 potential problem by defining it as a smell. The aim of MockObjects, surely?, is to support anything that can be thrown= =20 at it. Isn't just ignoring this kind of 'problem' limiting? The=20 MockObjects must be as complicated as the thing they are trying to Mock.=20 Ignoring parts of industry standard interfaces sounds like a death=20 sentence to me...=20 Having ideals is fine, sticking them to the end without compromise is=20 quite often stupid. Also in certain cirumstances Verifier can be involved in mantaining the=20 infinate loops and inherant stack overflow that occurs in these=20 circumstances, is this the sort of image a project that should be capable=20 of being used in the commerce commercial quality projects be projecting? As far as the JDBC problem goes could it be gotten around by storing the=20 parent references as the implemented interface rather than the Mock=20 reference? Simon., (My thoughts - NOT my employers) |
From: Steve F. <st...@m3...> - 2002-09-29 16:56:48
|
I've done a little tidy up in the Verifier. No major functional changes yet, but I've made verifyField() private -- unless someone wants it back. That static vector still gets up my nose, I've done some refactorings towards getting away from it but got a bit stuck, so I'd like to poll people again. It seems that the principal need for this is in the JDBC library where, for example, a mock statement references a mock connection and vice versa. I can't help feeling that we've let our desire for generality get out of hand and that we really shouldn't be writing code that daisy-chains statements and connections (or whatever). Similarly, I doubt that we should have one setup routine for a "cluster" of related objects, we should have several to implement multiple situations. I'd like to propose that a Verifiable loop is a code smell which implies that something has become too complicated, so we shouldn't compensate for it. Thoughts? Steve - - - - - - - - - - - - - - - Steve Freeman st...@m3... Programme chair OT2003. http://www.ot2003.org "Nonsense is just nonsense, but the study of nonsense is science." |
From: Steve F. <sm...@us...> - 2002-09-29 16:44:32
|
Update of /cvsroot/mockobjects/mockobjects-java/src/core/com/mockobjects/test In directory usw-pr-cvs1:/tmp/cvs-serv20790/src/core/com/mockobjects/test Modified Files: TestVerifier.java Log Message: Some tidy-up Initial refactorings towards avoiding static collection Hid verifyField() Index: TestVerifier.java =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/src/core/com/mockobjects/test/TestVerifier.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- TestVerifier.java 17 Sep 2002 14:51:11 -0000 1.2 +++ TestVerifier.java 29 Sep 2002 16:44:28 -0000 1.3 @@ -12,7 +12,7 @@ class OneVerifiable extends MockObject { private ExpectationValue myValue = new ExpectationValue("should fail"); - private int anotherField; + private int unusedField; public OneVerifiable() { myValue.setFailOnVerify(); @@ -27,6 +27,25 @@ class InheritVerifiable extends OneVerifiable { } + class LoopingVerifiable extends MockObject { + LoopingVerifiable myRef = this; + boolean inVerify = false; + + LoopingVerifiable() { + super(); + } + + public void setRef(LoopingVerifiable aRef) { + myRef = aRef; + } + + public void verify() { + assertTrue("Looping verification detected", !inVerify); + inVerify = true; + super.verify(); + inVerify = false; + } + } public TestVerifier(String name) { super(name); @@ -94,39 +113,16 @@ oneVerifiable.verify(); } - - class LoopingVerifiable extends MockObject { - LoopingVerifiable myRef; - boolean inVerify = false; - - LoopingVerifiable(LoopingVerifiable aRef) { - setRef(aRef); - } - - - public void setRef(LoopingVerifiable aRef) { - myRef = (aRef == null) ? this : aRef; - } - - public void verify() { - assertTrue("Looping verification detected", !inVerify); - inVerify = true; - super.verify(); - inVerify = false; - } - } - public void testNoLoopVerifySingleLevel() { - new LoopingVerifiable(null).verify(); + new LoopingVerifiable().verify(); } - public void testNoLoopVerifyMultiLevel() { - LoopingVerifiable a = new LoopingVerifiable(null); - LoopingVerifiable b = new LoopingVerifiable(a); + LoopingVerifiable a = new LoopingVerifiable(); + LoopingVerifiable b = new LoopingVerifiable(); a.setRef(b); + b.setRef(a); a.verify(); } - } |
From: Steve F. <sm...@us...> - 2002-09-29 16:44:32
|
Update of /cvsroot/mockobjects/mockobjects-java/src/core/com/mockobjects/util In directory usw-pr-cvs1:/tmp/cvs-serv20790/src/core/com/mockobjects/util Modified Files: Verifier.java Log Message: Some tidy-up Initial refactorings towards avoiding static collection Hid verifyField() Index: Verifier.java =================================================================== RCS file: /cvsroot/mockobjects/mockobjects-java/src/core/com/mockobjects/util/Verifier.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- Verifier.java 17 Sep 2002 14:51:11 -0000 1.4 +++ Verifier.java 29 Sep 2002 16:44:28 -0000 1.5 @@ -5,6 +5,8 @@ import java.lang.reflect.Field; import java.util.Vector; +import junit.framework.Assert; + /** * Helper class to verify all {@link com.mockobjects.Expectation Expectation}s * of an object. @@ -12,7 +14,7 @@ * methods to verify objects: * <ul> * <li>{@link com.mockobjects.util.Verifier#verifyObject(java.lang.Object) verifyObject(Object)}</li> - * <li>{@link com.mockobjects.util.Verifier#verifyField(java.lang.reflect.Field, java.lang.Object) verifyField(Field, Object)}</li> + * <li>{@link Verifier#verifyField(Field,Object,Vector) verifyField(Field, Object)}</li> * </ul> * These two methods can be used to verify any expectation to assert that * they still hold.<p> @@ -32,105 +34,58 @@ * {@link com.mockobjects.Verifiable Verifiable}, i.e.: by delegation. * @see com.mockobjects.Expectation * @see com.mockobjects.Verifiable - * @author <a href="mailto:fb...@us...">Francois Beausoleil (fb...@us...)</a> * @version $Id$ */ public class Verifier { private static Vector myProcessingObjects = new Vector(); - /** - * Public, no-op constructor. - * You should not need to instantiate this object, since all methods - * are <code>static</code>. - */ - public Verifier() { - super(); - } /** - * Calls {@link com.mockobjects.Verifiable#verify() Verifiable.verify()} - * on <code>aField</code>. - * This method will result in a no-op if <code>aField</code> does not - * implement {@link com.mockobjects.Verifiable Verifiable}. - * @param aField The field which needs to be checked. - * @param anObject The object in which <code>aField</code> is instantiated. - */ - static public void verifyField(Field aField, Object anObject) { - try { - aField.setAccessible(true); - Object fieldObject = aField.get(anObject); - - if (isVerifiable(fieldObject)) { - if (!myProcessingObjects.contains(fieldObject)) { - ((Verifiable)fieldObject).verify(); - } - } - } catch (IllegalAccessException e) { - junit.framework.Assert.fail("Could not access field " - + aField.getName()); - } - } - - /** - * Recursively verifies all fields of the passed object. + * Verifies all the fields of type Verifiable in the given object, including + * those inherited from superclasses. * @param anObject The object to be verified. */ static synchronized public void verifyObject(Object anObject) { - verifyObjectFromClass(anObject, anObject.getClass()); + verifyFieldsForClass(anObject, anObject.getClass(), myProcessingObjects); } - /** - * Verifies all fields of the passed object, as if it were of the - * passed class. - * This method will be called recursively for each superclass of - * <code>anObject</code>, until the - * {@link com.mockobjects.util.Verifier#isRootClass(java.lang.Class) isRootClass(Class)} - * returns <code>true</code>, at which point the recursion will stop. - * @param anObject The object on which to verify fields. - * @param aClass The apparent class of <code>anObject</code> - */ - static private void verifyObjectFromClass(Object anObject, Class aClass) { - if (myProcessingObjects.contains(anObject)) { - return; - } - if (isRootClass(aClass)) { + static private void verifyFieldsForClass(Object anObject, Class aClass, Vector alreadyProcessed) { + if (alreadyProcessed.contains(anObject) || isBaseObjectClass(aClass)) { return; } - verifyObjectFromClass(anObject, aClass.getSuperclass()); + verifyFieldsForClass(anObject, aClass.getSuperclass(), alreadyProcessed); try { - myProcessingObjects.addElement(anObject); - + alreadyProcessed.addElement(anObject); + Field[] fields = aClass.getDeclaredFields(); for (int i = 0; i < fields.length; ++i) { - verifyField(fields[i], anObject); + verifyField(fields[i], anObject, alreadyProcessed); } } finally { - myProcessingObjects.removeElement(anObject); + alreadyProcessed.removeElement(anObject); + } + } + + static private void verifyField(Field aField, Object anObject, Vector alreadyProcessed) { + try { + aField.setAccessible(true); + Object fieldObject = aField.get(anObject); + + if (isVerifiable(fieldObject) && ! alreadyProcessed.contains(fieldObject)) { + ((Verifiable)fieldObject).verify(); + } + } catch (IllegalAccessException e) { + Assert.fail("Could not access field " + aField.getName()); } } - /** - * Returns a <code>boolean</code> indicating whether or not the passed - * object is {@link com.mockobjects.Verifiable Verifiable}. - * @returns <code>true</code> if <code>anObject</code> implements - * {@link com.mockobjects.Verifiable Verifiable}, <code>false</code> - * otherwise. - */ private static boolean isVerifiable(Object anObject) { return anObject instanceof Verifiable; } - /** - * Returns a <code>boolean</code> indicating whether or not the passed - * class is the root class. - * The root class is where recursive searching for fields should stop. - * Usually, this would be {@link java.lang.Object}. - * @returns <code>true</code> if <code>aClass</code> is the root class, - * <code>false</code> otherwise. - */ - private static boolean isRootClass(Class aClass) { + private static boolean isBaseObjectClass(Class aClass) { return aClass.equals(Object.class); } } |
From: Jeff M. <je...@mk...> - 2002-09-19 08:37:37
|
There isn't a generic method for throwing exception but there are place in the code which support throwing specific exceptions. e.g. http://www.mockobjects.com/javadoc/1.3/com/mockobjects/sql/CommonMockMultiRowResultSet.html#setupThrowExceptionOnGet(java.sql.SQLException) Yes exceptions should be tested for (Though I suspect that it's not done nearly enough). This is one of the big advantages of mocks. On Thu, 2002-09-19 at 06:33, denis krizanovic wrote: > > Hi - > > is there a way to get a method to raise an exception in a mockobject? > But then, should I be doing this? should I be trying to test the > exception handling code I've written? > > regards, > dk- > > > > ------------------------------------------------------- > This sf.net email is sponsored by:ThinkGeek > Welcome to geek heaven. > http://thinkgeek.com/sf > _______________________________________________ > Mockobjects-java-dev mailing list > Moc...@li... > https://lists.sourceforge.net/lists/listinfo/mockobjects-java-dev -- jeff martin information technologist mkodo limited mobile: 44 (0) 78 5547 8331 phone: 44 (0) 20 2226 4545 email: je...@mk... www.mkodo.com |