From: brett l. <bre...@gm...> - 2012-06-08 14:34:15
|
On Fri, Jun 8, 2012 at 10:12 AM, Erik Vos <eri...@xs...> wrote: > Stefan, > > Thanks for your informational lecture. Unit testing is an old concept, of > which I was well aware, but I have not really used it in practice > throughout my professional life, and certainly not in an OO environment. > Mocking and stubbing are new to me, but I understand their purpose now. > > The (superficial) impression I'm getting is that it is easy to test the > things that are hard to get wrong, but a lot more difficult to test complex > stuff that is easy to get wrong. > For instance, how would you test that on a phase change a new train type > does become available? Or does such a test already go beyond unit testing? > This is an area where it's important to have good OO design. The idea would be to mock/stub out dependencies of the Phase object, possibly all the way up to a mocked up GameManager. Then you'd instantiate a real Phase object (or set of Phase objects) with the mocked input data/objects. Finally, you'd call the sequence of methods that represents a phase change, and validate that the Phase object or the other appropriate mocked objects contain the data you expect. It's more complicated, and not everything is easy to mock or test, but sometimes just by going through the process of trying to test it you might realize that there's a change needed to the code that will make it more testable and less bug-prone. This extends into TDD where, by designing the test up front, you're spending time thinking through the design and the tests describe how you'd expect the code to operate. > Of course, a good design reduces the existence of such complexities as much > as possible. But we all know that Rails has grown in a different way. > Hopefully Rails 2.0 will improve that a great deal. > > Erik. > ---Brett. >> -----Original Message----- >> From: Stefan Frey [mailto:ste...@we...] >> Sent: Friday, June 08, 2012 2:56 PM >> To: Development list for Rails: an 18xx game >> Subject: Re: [Rails-devel] Mocking library chosen >> >> Brett and Erik: >> >> Some comments see below: >> >> * Test location: >> Yes as mentioned below in Rails 2.0 there are now two top-level > directories: >> /src >> /junit >> >> where below src is all of the existing code and below junit everything for >> testing will be added. >> >> The hierarchy in junit will mirror that of src so that the tests are easy > to find. >> >> * Code/Release size: >> Code size of Rails (below src) itself is not effected directly, but I hope > to >> reduce the code by removing code duplication. However this will be easily >> offset by new functionality. >> >> The release size does not change as well as neither the test jars nor the > test >> source is part of the release package. >> >> * Some more details for Erik: >> >> * Unit tests >> The main idea behind unit tests is that of "never change a running > system". >> This is correct observation, however not changing a system is bad too, >> because this will easily lead to code duplication. So unit tests allow to > "change >> a system, but keep it running". >> If you have automated tests that run before you changed the systems and >> after you changed the system, in fact you kept the system running, but you >> are still able to re-factor >> >> Another nice term I found for unit tests is "necessary waste": >> For perfect coders it is a waste of time, but as most of us are not of > that kind, >> it is necessary. >> >> Actually unit testing and mocking is not that is not too difficult > conceptually. >> Someone who got a graphical 1830 moderator working as you did, should >> have no problem to understand them. >> >> Unit testing itself mirrors the tests which are possible in an interpreted >> language: >> A) Write a function add(x,y) = x+y >> B) Call the function with foo(10, 15) in the console >> C) Check manually that the result is 25 and that no error was raised >> >> A unit test is: >> A) Write a function add(x,y) = x+y >> B) Write a test addTest(): assert(add(x,y),25) >> C) Start the test runner which reports passed if add(x,y) == 25 or fails > if >> add(x,y) != 25 or returns an error. >> >> If you reverse A and B, thus you write the test before the actual code it > is >> called TestDrivenDevelopment (TDD) >> >> * Mocks >> Usually you want to test classes as independent entities. And you want to >> configure setup the class to test alone. >> >> However often a method call requires or the construction of a class > requires >> other objects. >> >> A mock library allows you to create a "phantom" object that pretends to be > a >> object of the class, however actually does nothing at all. >> >> Example: >> If you create a Player mock using >> Player p = mock(Player.class) >> the variable p now can be passed as a player object and all methods of a >> player class can be called on p and everything compiles correctly. >> But in fact nothing happens if one calls methods on p. >> >> Or more precise nearly nothing happens: >> It is possible to ask the mock later if the methods got called. >> >> Assume that you test a function that adds a train to a player. It has two > tasks: >> A) Add the train to the player >> B) Change the owner of the train to the player >> >> Now you can either create fully initialized player and train objects. >> >> However it might be easier to mock both player and train using Player p = >> mock(Player.class) and Train t = mock(Train.class) >> >> The function is called transferTrain(Player p, Train t). >> >> You pass both mocks to the function and then check that the function has >> called the according methods of player and train: >> >> verify(player).addTrain(t) >> verify(train).setOwner(p) >> >> Here it is easy to see why a test with fully initialized objects might be > a little >> stronger: >> Then you would check that the train and player really have changed e.g. >> by assert(train.getOwner(),p), however this is would be already a joint > test >> of both the function under test and the train object. >> >> There is a related technique called stubbing: In that case you not only > create >> a phantom object, but more a skeleton object. A stub for a Player would be > a >> minimal but working implementation. >> >> Mockito supports simple/basic stubbing as well: Assume that function would >> only succeed if the addTrain method of player returns a True value. This > is >> possible by instructing the mock object to return that on a call of > addTrain by: >> when(p.addTrain(t)).thenReturn(True) >> >> Feel free to ask more questions if you like. >> >> Stefan > > > ------------------------------------------------------------------------------ > Live Security Virtual Conference > Exclusive live event will cover all the ways today's security and > threat landscape has changed and how IT managers can respond. Discussions > will include endpoint security, mobile security and the latest in malware > threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/ > _______________________________________________ > Rails-devel mailing list > Rai...@li... > https://lists.sourceforge.net/lists/listinfo/rails-devel |