- Design strategies for implementing a suite of algorithms in C++ - 2 Updates
- Triple vodka - 2 Updates
- TDD considered harmful - 9 Updates
- Unit testing (Re: Putting project code in many different files) - 9 Updates
- Minecraft - 3 Updates
Paul <pepstein5@gmail.com>: Jan 31 01:11PM -0800 I am trying to present in C++ a set of algorithms for solving a problem. It is intended that the set of algorithms will expand. So far, I have five basic algorithms for solving the same problem. The end goal (so far) is to develop a simple user interface at the console so that the user can provide the parameters, together with a string indicating the name of the algorithm, and my code will provide the solution (and possibly the runtime). Four of these algorithms are simple conceptually (although the maths is complicated). However, the fifth algorithm is much more involved because it has four or five features which can be absent or present so the fifth algorithm can really be thought of as 16 or 32 algorithms. (The vagueness is because I haven't decided which features to be settable by the user). If I only had the first four to worry about, the design could be very simple. I would have a pure abstract class as a base of all the algorithms. Finding the solution is a virtual function which depends on the algorithm selected by the user. class Algorithm{ public: Algorithm(some parameters); virtual void findSolution() = 0; } class Method1 : public Algorithm { public: Method1(some parameters) : Algorithm(some parameters) { } void findSolution() { } } Methods 2, 3 and 4 could have the same design as Method 1. But the complex method, method 5, makes it harder to design elegantly. I could try class Method5 : public Algorithm { bool flag1; bool flag2; ... public: Method5 (some parameters, bool someFlag, bool someOtherFlag): Algorithm(some parameters), flag1(someFlag), flag2(someOtherFlag) { } void findSolution() { if(flag1) doThis; if(flag2) doThat; //// } } However, now for Method 5, the approach no longer looks elegant. Any ideas? I'm deliberately being vague about the precise nature of the problem because I want to decouple the maths side from the design side and I definitely don't want help on the maths side. Many thanks, Paul |
Paul <pepstein5@gmail.com>: Jan 31 01:15PM -0800 On Sunday, January 31, 2016 at 9:12:05 PM UTC, Paul wrote: > } > However, now for Method 5, the approach no longer looks elegant. > Any ideas? I'm deliberately being vague about the precise nature of the problem because I want to decouple the maths side from the design side and I definitely don't want help on the maths side. Sorry, I missed the semicolons off the class definitions. And I need a virtual destructor too. Paul |
woodbrian77@gmail.com: Jan 31 12:16PM -0800 On Saturday, January 30, 2016 at 5:16:44 PM UTC-6, Mr Flibble wrote: > Is having a triple vodka a thing? > /Flibble It's probably a siren song (deceptively alluring) thing. Brian Ebenezer Enterprises - "Do not get drunk on wine, which leads to debauchery. Instead, be filled with the Spirit." Ephesians 5:18 http://webEbenezer.net |
Daniel <danielaparker@gmail.com>: Jan 31 12:39PM -0800 > "Do not get drunk on wine, which leads > to debauchery. Instead, be filled with the Spirit." Ephesians 5:18 "Drink no longer water, but use a little wine for thy stomach's sake and thine often infirmities." 1 Timothy 5:23 |
Paavo Helde <myfirstname@osa.pri.ee>: Jan 31 02:14AM +0200 On 31.01.2016 1:11, Mr Flibble wrote: > If you don't think about or don't understand class invariants (which I > suspect is the case for most TDD adherents) then your existing > non-trivial designs really will be god-awful. You have found another straw man to attack. Why should TDD prohibit private methods or attempt to test them? The private methods are declared private because they are not guaranteed to maintain class invariant; declaring them private avoids other code calling them accidentally (including any test code). > TDD is the enemy of encapsulation. TDD really is a bad idea. Non sequitur. Maybe you should stick to sausages? |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jan 31 01:58AM +0100 On 1/31/2016 1:14 AM, Paavo Helde wrote: > declared private because they are not guaranteed to maintain class > invariant; declaring them private avoids other code calling them > accidentally (including any test code). Why should TDD not test private methods? I understand from the "avoids … test code [calling them]" that you're thinking of a case where somehow the private methods are inaccessible to test code, but why should they be? I'm just asking. I've not been into TDD very much. If it's really the case that it's not applicable to private parts, or e.g. that there's no idea of hierarchy and nesting and levels, then it seems that it's much in need of modernization of tooling and/or methodology, like some COBOL-like brute animal from the distant past? Cheers, - Alf |
"Öö Tiib" <ootiib@hot.ee>: Jan 30 06:06PM -0800 On Sunday, 31 January 2016 02:58:35 UTC+2, Alf P. Steinbach wrote: > I understand from the "avoids ... test code [calling them]" that you're > thinking of a case where somehow the private methods are inaccessible to > test code, but why should they be? It is generally more difficult if external software (including tests) accesses the internal details and assumes those. So testing private methods may make functionality or performance improving difficult, froze the design and add unneeded internal paranoia bloat to class implementation. > idea of hierarchy and nesting and levels, then it seems that it's much > in need of modernization of tooling and/or methodology, like some > COBOL-like brute animal from the distant past? Code of unit tests is typically longer than code of class itself. Sometimes order of magnitude longer. Now we maintain the class. Tests fail. May be those are false positives or may be those are not? May be the test assumed something unneeded? May be it was needed but the writer of test was just lazy to achieve same situation from public interface? If the tests do not cheapen the maintenance then TDD is applied in harmful manner. |
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Jan 31 03:42AM On 31/01/2016 00:14, Paavo Helde wrote: > accidentally (including any test code). >> TDD is the enemy of encapsulation. TDD really is a bad idea. > Non sequitur. Maybe you should stick to sausages? It isn't a non-sequitur at all: do you understand encapsulation? TDD wants lots of testable small (atomic) public methods to test but every public method you add actually REDUCES the encapsulation of a class whilst adding a private method does not sausages. /Flibble |
"Öö Tiib" <ootiib@hot.ee>: Jan 30 09:02PM -0800 On Sunday, 31 January 2016 05:42:12 UTC+2, Mr Flibble wrote: > wants lots of testable small (atomic) public methods to test but every > public method you add actually REDUCES the encapsulation of a class > whilst adding a private method does not sausages. That is not true at all. Typically TDD just tests that public interface of a class does what it claims in interface specification. So one using TDD wants to reduce public interface to minimum needed, not to make private members public. |
Ian Collins <ian-news@hotmail.com>: Jan 31 09:18PM +1300 Mr Flibble wrote: > is impossible to maintain a class invariant except WITHIN a SINGLE > public method which has the effect that some of your public methods MUST > be unnecessarily large/complex. TDD and using private methods are orthogonal. Does your local butcher having a secret recipe for his sausages prevent you from taste testing them? -- Ian Collins |
Paavo Helde <myfirstname@osa.pri.ee>: Jan 31 11:54AM +0200 On 31.01.2016 2:58, Alf P. Steinbach wrote: > I understand from the "avoids … test code [calling them]" that you're > thinking of a case where somehow the private methods are inaccessible to > test code, but why should they be? TDD means test-driven development. This means that when adding any feature or fixing a bug one first writes a test which *uses* the needed part of the software and first fails. Then code is added or modified so that the test passes. The resulting code is refactored to become simpler and more maintainable, while still passing the test. Finally the test is added to the automatic test suite (if possible and feasible). Note that in this description no methods or classes are mentioned, this is all about *usage*. And usage happens only via public methods. What happens inside the class is of no concern in TDD, as long as the tests succeed. Note that the concept of TDD is a bit orthogonal to unit testing. If the TDD-tested feature is small enough, the TDD test may be added to the unit test suite. If it is too large, it might be added to the integration test suite. Or if it is used for development only (the last 'D' in TDD), then it is not added anywhere. Private methods are more a topic for code reviews, unit testing and mocked interfaces. There are probably projects where private methods are considered units and tested accordingly. I have never done such things, so have no experience, but I believe this could easily become an overkill and counter-productive. For example, the refactoring step present in the TDD cycle would become much harder. Cheers Paavo |
4ndre4 <4ndre4@4ndre4.com.invalid>: Jan 31 06:45PM On 30/01/2016 23:11, Mr Flibble wrote: > If you eschew private methods and instead make everything public That's not what TDD is. -- 4ndre4 "The use of COBOL cripples the mind; its teaching should, therefore, be regarded as a criminal offense." (E. Dijkstra) |
4ndre4 <4ndre4@4ndre4.com.invalid>: Jan 31 06:55PM On 31/01/2016 03:42, Mr Flibble wrote: [...] > TDD > wants lots of testable small (atomic) public methods to test but every > public method you add actually REDUCES the encapsulation of a class No, it doesn't. Sorry, but you haven't understood what TDD is. The fact that you can test each individual electronic component available on the market in isolation, does not mean that you are esposing any details about the interaction between those components, on any electronic board you can build with them. Remember that there are languages (such as Python, JavaScript, etc..) where there is no concept of "private method". Private methods are just a convention. -- 4ndre4 "The use of COBOL cripples the mind; its teaching should, therefore, be regarded as a criminal offense." (E. Dijkstra) |
Jerry Stuckle <jstucklex@attglobal.net>: Jan 30 09:40PM -0500 On 1/30/2016 6:09 PM, Ian Collins wrote: >>> being able to control the behaviour of the B methods it is using. >> But a mock of B may or may not behave the same. > A mock of B will behave exactly as the tests define it. Which may or may not be the actual behavior of the class. >> the interaction between the two is part of the unit test of A. > Consider the case where B isn't part of the project code but a third > part library. We're not talking about something that isn't part of the project. -- ================== Remove the "x" from my email address Jerry Stuckle jstucklex@attglobal.net ================== |
Ian Collins <ian-news@hotmail.com>: Jan 31 07:14PM +1300 Jerry Stuckle wrote: >>> But a mock of B may or may not behave the same. >> A mock of B will behave exactly as the tests define it. > Which may or may not be the actual behavior of the class. Which would be pretty f'ing useless. -- Ian Collins |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jan 31 08:14AM +0100 On 1/31/2016 7:14 AM, Ian Collins wrote: >>> A mock of B will behave exactly as the tests define it. >> Which may or may not be the actual behavior of the class. > Which would be pretty f'ing useless. No, it's necessary to test the pieces of each class working together, at the level of unit testing. Traditional integration testing is at a higher level. If this is not done then one risks that the tests for B do not guarantee that B will behave the same as the mock-up of B for the A testing, e.g. due to these tests not completely nailing down the B behavior in all test cases and test sequences. As a perhaps silly example, B might be a string class with encoding checks. One of which is `is_ascii` (comes to mind because I've recently posted about such a function). Perhaps tests for B fail to test `is_ascii` for the edge case of empty string. Perhaps the mockup returns `false` in this case, causing testing of A with mockup to succeed, while the real B and design intention is return of `true`. To discover that the B tests are incomplete, the real B needs to be exercised in its intended environment. It's like, no plan survives contact with the enemy. No set of tests survives contact with reality, although it can survive contact with just mockups, which after all are coded to just support the tests at hand, aren't they? So in that respect Jerry Stuckle is right, as I see it. But Jerry can possibly be wrong regarding his statement that "before you can thoroughly test A you have to ensure B works". It depends very much on what "thoroughly" means here. I suspect that Jerry's intended meaning of "thoroughly" was and is that A is tested with the real B, while Ian's meaning of "thoroughly" is that A is sort of thoroughly tested with a mock-up of B, and that testing with real B is something else, maybe called something else? Cheers, wondering, - Alf |
Ian Collins <ian-news@hotmail.com>: Jan 31 08:42PM +1300 Alf P. Steinbach wrote: > meaning of "thoroughly" is that A is sort of thoroughly tested with a > mock-up of B, and that testing with real B is something else, maybe > called something else? You can't thoroughly test A with a real B because you can't control the behaviour of the real B's methods. They will do what they naturally do, but if your test requires one of B's methods to fail in some way you either have to pollute B with the code to synthesise a failure, or mock it. Yes at some point (typically in integration testing) you will be testing the real code in combination, but for unit testing A, you need to mock B. -- Ian Collins |
Jorgen Grahn <grahn+nntp@snipabacken.se>: Jan 31 08:11AM On Sat, 2016-01-30, 4ndre4 wrote: >> I agree -- for most cases. But sometimes (let's say 5%) some other >> consideration makes following the convention suboptimal. > Such as? Actually, the considerations are always the same: readability, maintainability and so on. I meant to say sometimes the 1:1 rule doesn't optimize for that. My example elsewhere in the thread was a set of small sibling classes which are similar, but cannot be a template. But I'm sure there are other examples. /Jorgen -- // Jorgen Grahn <grahn@ Oo o. . . \X/ snipabacken.se> O o . |
Jorgen Grahn <grahn+nntp@snipabacken.se>: Jan 31 01:23PM On Fri, 2016-01-29, Ian Collins wrote: > for a unit. > I think the case is clearer for encapsulation than it is for > inheritance. I will always mock an encapsulate class, with inheritance ^^^ (I assume you meant "encapsulated" there.) To me, that sounds, well, extreme. Where would you stop? If you don't mock std::string, std::map etc, what's the difference between them and one of your own classes (which you have tested elsewhere?) class Foo { Bar bar; ... }; I've always figured bar is an implementation detail of Foo; that its state is part of Foo's state. So I ignore it when I'm testing Foo (assuming Bar itself doesn't do I/O or something). Question: if these are two different ways of thinking about unit tests for classes, do they have names? Can I refer to books, when arguing with coworkers about how to proceed with the testing? It's so frustrating when you only have your own opinions and prejudices as a starting point. (One entry point could perhaps be this and related entries: http://c2.com/cgi/wiki?StandardDefinitionOfUnitTest but there's no consensus there either.) I've tried to think about benefits of mocking the encapsulated bar ... one thing would be that you make it explicit what contract Foo expects Bar to keep. You can also document what happens to Foo if Bar doesn't keep the contract, but I'm unsure if that has any value. I suspect you can list better arguments. You gave one below: > Whether a base/encapsulated class class has been tested is > irrelevant, you still need a mock object to simulate error > conditions. Hm ... yes, I can easily see memory allocation failures fitting in there. But is there anything else, typically? It seems to me that a class Bar which doesn't need mocking for /really/ obvious reasons like it's doing I/O, will fail due to - programming errors (covered by Bar's unit tests) - incorrect usage (covered by Foo's unit tests) - memory allocation problems /Jorgen -- // Jorgen Grahn <grahn@ Oo o. . . \X/ snipabacken.se> O o . |
Jorgen Grahn <grahn+nntp@snipabacken.se>: Jan 31 01:32PM On Sat, 2016-01-30, 4ndre4 wrote: >> them in the same file, once you finish with A, you need to go back and >> test B again, because it's file has changed. > What?! Big deal; you run your unit tests constantly anyway. (In my case; "make check" and the tests are built and executed, and they print just three lines of "everything is fine" if all tests still pass.) /Jorgen -- // Jorgen Grahn <grahn@ Oo o. . . \X/ snipabacken.se> O o . |
Jorgen Grahn <grahn+nntp@snipabacken.se>: Jan 31 02:00PM On Sat, 2016-01-30, Jerry Stuckle wrote: >> when anything changes in the repository. So there is no problem with >> testing B again, this will be done uncountable times. > Yes, if the testing is automated (and it is in most large projects), There is no reason it should be manual in small or medium-sized projects ... building and running unit tests is one of the most easily automated tasks I can think of, provided you have half-decent tools. (Except if you're cross-compiling and this is the only thing you want to run on the host. But then doing it manually would be even harder.) > that is true. But it still takes time and manpower to ensure the tests > get run and to verify the results. IMO, one major key to get unit tests to work in practice is: - They're trivial to run as part of the build (e.g. everyone on the project knows "make all test" will always run the correct set of tests). - They don't take a lot of extra time, compared to plain incremental builds. - The output when everything is fine doesn't fill your screen with information you won't read ("hey, did you know these 4711 tests all passed -- again?" followed by a complete list). Other things (like reports and statistics from nightly builds) are optional, but without all three bullets above, unit tests will never work well (IMO, of course). /Jorgen -- // Jorgen Grahn <grahn@ Oo o. . . \X/ snipabacken.se> O o . |
4ndre4 <4ndre4@4ndre4.com.invalid>: Jan 31 06:42PM On 30/01/2016 22:28, Jerry Stuckle wrote: [...] > It does matter, and it's not at all unusual for a class to have other > classes as members. You start out testing single classes. Then you can > test classes which include the first class. No, it doesn't matter in the least. Tests are normally executed in random order. The unit tests for class A are supposed to test ONLY the behaviour for class A and not assume any dependency. If any dependency is needed, it should be mocked. >> running the tests contained in SomethingTest.cpp? > Please read the thread. We are talking about when there are multiple > classes in one file. My question stands. -- 4ndre4 "The use of COBOL cripples the mind; its teaching should, therefore, be regarded as a criminal offense." (E. Dijkstra) |
"Öö Tiib" <ootiib@hot.ee>: Jan 30 05:07PM -0800 On Saturday, 30 January 2016 20:36:17 UTC+2, David Brown wrote: > the sort. It is believable that of the people who responded to that > survey, that about 50% said that when they used some sort of wireless > communication on a project, it included WiFi. But that's all. It may be that there are more 8 bit MCUs sold than 10% but the overall development effort share for such very constrained platforms can be very likely only around 10% or less. > embedded systems run Android, when in reality microcontrollers that are > not powerful enough to run Android outsell ones that are by a factor of > something like 10,000 to 1. The survey can be skewed in one or other way, I do not also take it as full truth. May be the Linuxes are not so common as it seems from that page but even with next to nothing Micrium RTOS (µC/OS-II, III) we can do multi-threading and dynamic resource management just fine if it is needed. It is wise to keep things as simple and robust as possible (but not simpler than possible). > Cortex M) are very much on the increase. This makes it a lot more > realistic to use "big system" features of C++ in a wider range of > embedded systems. It seems to be the trend. Embedded software is more complex and "wise" so development cost affects price of end products more. Couple cents for more powerful MCU can help to cut some tens of thousands from development cost thanks to better tools and also allow bigger ease of improving the product with software updates later. |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jan 31 02:32AM +0100 On 1/31/2016 2:07 AM, Öö Tiib wrote: > It is wise to keep things as simple and robust as possible (but not simpler > than possible). Oh, Einstein rears his ugly head again, LOLs. :) Cheers, & happy weekend!, - Alf |
David Brown <david.brown@hesbynett.no>: Jan 31 03:36PM +0100 On 31/01/16 02:07, Öö Tiib wrote: > It may be that there are more 8 bit MCUs sold than 10% but the overall > development effort share for such very constrained platforms can be very > likely only around 10% or less. That's another matter, of course. All sorts of statistics can be produced to say all sorts of things. My main point is that it nonsense to think that 50% of MCU's are used with WiFi, no matter how you count it (units shipped, developers using them, number of projects, dollars, whatever). I believe we agree on the basic points here anyway. > cents for more powerful MCU can help to cut some tens of thousands > from development cost thanks to better tools and also allow bigger > ease of improving the product with software updates later. Certainly that's the trend - though I am not sure that it cuts development costs so much in practice. The most powerful micros give you new opportunities (such as the freedom to use C++), but also make some things more complex. When you use an AVR with a crystal reference, the chip starts up running at the speed you picked with the choice of crystal. When you use an M4 with a crystal reference, you usually have a startup code enabling the oscillator, waiting for it to stabilise, then sorting out your PLL's, dividers, clock enables, etc., for all the different parts of the system. The ability to use 32-bit integers and basic maths without worrying about the time and space costs does not greatly reduce the effort. And then you get the boss and/or customer and/or company salesman saying "since you have such a powerful microcontroller, let's add /this/ feature to the software..." :-) |
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page. To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com. |
No comments:
Post a Comment