When to stop making tests in TDD [closed] - tdd

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 years ago.
Improve this question
I develop Tetris game using TDD. For now I'm testing isEmpty() method for Field class.
I have alredy written three test cases:
#Test
public void shouldIsEmptyMethodReturnTrueForEmptyField() {
Field field = Field.createStandartEmptyField();
assertTrue(field.isEmpty());
}
#Test
public void shouldIsEmptyMethodReturnFalseIfCellInLowerLeftCornerIsFilled() {
Field field = Field.createStandartEmptyField();
field.fillCellAt(0, 0);
assertFalse(field.isEmpty());
}
#Test
public void shouldIsEmptyMethodReturnFalseIfCellInLowerRightCornerIsFilled() {
Field field = Field.createStandartEmptyField();
field.fillCellAt(field.getWidth() - 1, 0);
assertFalse(field.isEmpty());
}
And my isEmpty() method looks like this
public boolean isEmpty() {
if (isFilledCellAt(0, 0)) {
return false;
}
if (isFilledCellAt(getWidth() - 1, 0)) {
return false;
}
return true;
}
So I can farther write new test cases to test every combination of filled cells in tetris field. When I have to stop? What is the right way to test isEmpty() method of tetris Field class using TDD?

Equivalence partitioning can help you reduce the number of test cases to write. Here, you could for instance have 3 cases : [NoCells, OneCell, ManyCells].
Boundary Value Analysis is a complementary approach, although it is more applicable to methods that take free input. Here, the context doesn't really allow going below or beyond extreme edges (-1 cells or > than number of cells in a row).
If you really want to test combinations of cases, Property-based Testing or other automated test data generation methods might be what you're looking for.

I think this is largely based on experience, and there's no easy way to determine your answer. When writing tests I look at:
Coverage. Have I covered all the code paths ? Or at least those that I feel will be the potential cause of issues
Inputs and outputs. Have I covered all the likely combinations of inputs and outputs. In some scenarios, this is not going to be practical (e.g. if your method takes an integer to add to another, do you pass in 1,2,3....)
Edge cases. Does my code handle boundary conditions properly ?
So as I write my code, I have a feel for the problematic scenarios that I need to assert, and whether each line of code I've written is covered in some fashion. If I have a sizable number of input/output scenarios to cover, I would consider implementing a table of inputs/outputs to assert on and let the test framework exercise each in total (e.g. Groovy's Spock makes this very easy. Junit's Parameterized Tests is another implementation of this)

This will largely be a personally preference. However if you want to follow strict TDD, then you will want to stop once you can no longer write a broken test. You start by writing a test that fails, and write exactly enough code to pass that test. Then you write the next failing test and continue to update your code and refactor until everything is green. If you can no longer write a failing test, then you don't need to write anymore tests.

Related

I'm Trying To Figure Out: What prevents you from "gaming" unit tests if you're doing them right?

So, whenever I attempt to learn Test Driven Development something always bothers me and I figure there must be something fundamental that I am missing about how unit tests and the process of test-driven-development are supposed to be "done."
Okay, so let's say you have a simple Class, called Calculator. Let's say you have one method that you are attempting to "drive" with your tests. The method is called "Add". It is supposed to take two numbers and give you the sum of those two numbers.
Let's say the test looks like this:
[TestMethod]
public void AddingTwoAndThreeEquals5()
{
var myCalulator = new Calculator();
var result = myCalulator.Add(2, 3);
Assert.AreEqual(5, result);
}
Makes sense as a test, right?
Let's say this is my implementation choice:
public int Add(int a, int b)
{
return 5;
}
This works. The test will pass. Hey, the implementation is even about as simple as it gets--no redundancy whatsoever. How could you refactor that? You're "Green" and good to go! :)
But that is obviously a horrible implementation choice! It will fail almost instantly in Production (just imagine the use-case where "2" and "2" are "Added" together to get "5"--the thought! gasp)! If Test-Driven-Development is just focused on getting the tests to pass as quickly as possible, what is to prevent you from "gaming" the test like this? Surely this is not the quality of code Test Driven Development is supposed to produce, but where is it expected that the code would be corrected to be more "flexible" as it would obviously need to be to function even remotely well in Production?
Do there just need to be enough tests that getting them all to pass at once while doing things like this would not be possible? Is this the sort of thing that should be caught on a code-review with a peer before check-in? Is it just up to the developer to be reasonable and know that this could not possibly pass muster in Production even if it technically makes the test pass--in other words, is the developer just to use common sense to know what is and is not in the spirit of the process even if they technically are getting the tests to go to "Green"?
This is obviously an extreme example as no one would likely check code like this in. But what about Test Driven Development is supposed to prevent "driving" code like this? Its a conceptual stumbling block that I always have when I undertake to learn Test Driven Development and I feel like there is just something fundamental that I am not getting about what Test Driven Development is and is not supposed to "do" for us.
Thank you so much for bearing with me through my question!
Cheers! :)
This works. The test will pass. Hey, the implementation is even about as simple as it gets--no redundancy whatsoever.
Ah - but that's not true, you do have duplication here, and you need to see it.
public int Add(int a, int b)
{
return 5;
}
becomes
public int Add(int a, int b)
{
return 2 + 3;
}
becomes
public int Add(int a, int b)
{
assert a == 2;
assert b == 3;
return a + b;
}
Now the duplication is removed, and you can move on to the next test.
In practice, Beck uses "refactor" somewhat differently from Fowler's definition. In Beck's examples, if a change keeps the tests passing, it counts as a refactor even though the behavior in examples-you-aren't-testing-yet might change.
So we'd expect Beck to skip that last example, and proceed directly to:
public int Add(int a, int b)
{
return a + b;
}
I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence -- Kent Beck, 2008
Now, today's picture is a bit more complicated than that. Rough timeline: Kent (re)introduces test first programming when he begins writing about Extreme Programming (XP). Test Driven Development by Example is published a few years later, and by then the ideas (a) have already been spread and (b) have a number of different interpreters, with the own interpretations, goals, agendas etc.
We've got a lot of different ideas like "Simplest Thing", and "You Aren't Gonna Need It" that are common in the same communities, and people try out applying them to the tests and refactoring of TDD. Sometimes the slogans translate well, other times... not so much?
what about Test Driven Development is supposed to prevent "driving" code like this?
So the first thing to recognize is that "Driven" here is pure marketing garbage. There was a time when people believed that the design followed from the tests - but that didn't really work out, and in retrospect there were some pretty clear tells that it wasn't going to work out. Michael Feathers referred to this period as the Look, Ma, no hands era.
There are some cases where the implementation of the tests gives hints that there is a problem in your design (too much irrelevant set up is a common example). But structure invariant tests simply can't "drive" a structure - all "refactorings" are reversible.
What those tests can do is alert you if a small code change you make to improve the design changes any of the behaviors you measuring. If so, you can evaluate and remedy the contradiction at once (usually by reverting the small change that introduced the fault) and move on.
(Contrast this with a "test first" approach, where the test fails until you have the entire design typed in. That same test may then still fail, and now you have to go digging for the fault you introduced. The shortened feedback loop is the big idea of TDD.)
Now, off the top of my head, a better test is to have two randomly generated integers and the result of Add is the result of adding the first and second integer. Suddenly the test becomes way more robust.
Test Driven Development / Test Driven Design is a design practice, not a test practice. Defending against the malfeasance of the Enterprise Developer from Hell is not a TDD goal.
Your cheat is actually something that is quite common while driving the design ("making the test pass, committing any crime necessary"). It works with 2 and 3 and even with a virtually infinite number of other combinations of numbers. The cheat is resolved by either getting rid of duplication as VoiceOfUnreason shows (5 is duplicated in your test and in your production code, replacing it by a+b removes this duplication) or by "triangulation" (writing more tests that expect different results). TDD is not about being "artificially stupid" or even malicious.
The duplication here is in the test and the production code. Notice the following two lines
Assert.AreEqual(5, result);
return 5;
To remove this duplication you need to think about making your code more generic. As #EricSchaefer rightly mentioned, to make the code more generic you need to follow the triangulation technique.

Cleaning Functions containing if statements

I hope that this is the right place to ask (if not please tell me). I am currently trying to create a game while following Robert C. Martins book "Clean Code" in an effort to improve the readability of my code. I am not totally happy with how many of my functions work however, as more often than not I will need to check various variables before I execute a command. For example:
private void checkScoreAndIncreaseDifficulty() {
if(eater.getScore()%400==0){
world.increaseDifficulty();
}
According to the book I am following functions should only do one thing, but when an "if" statement is involved the functions purpose (to me) seems to naturally increase. The name of the above function is checkScoreAndIncreaseDifficulty which is quite clearly two things but I cannot think of how to reduce it. I feel the solution is very simple but it just is not coming to me. Any advice would be appreciated.
Names of functions are supposed to describe what the function does,not how achieves something. So naming checkScoreAndIncreaseDifficulty is wrong imo. It should be something similar to IncreaseDifficulty. Moreover if you do not feel that names of your code elements do not give enough insight on what the function / class/ variable does, you can always comment your code. :)
Also, the "correct" place to write your conditional statement depends entirely on your application : if difficulty will increase only if the user satisfies some condition, then that condition should be included in the function that increases difficulty. If the difficulty might be increased by other means, then the function should only execute actions that increase difficulty and leave the decision whether to increase difficulty or not to the caller.
I don't see any way to make this method cleaner than it is. It checks the score, and then hands of the increaseDifficulty to another method. What more can you ask?
I think there's also a risk in trying to make methods as minimal as they can possibly be. At some point, you need to dig through a dozen methods and functions spread through your entire codebase in order to understand what a single method or function does. That's no good either.
As far as I'm concerned, your code hits the sweet spot. The cumbersome name is indeed an issue, but not a serious one, and easy enough to fix, if you really want to.
Why don't you use two separate functions(you can do it also with only first function) like this:
private bool isScoreEnough() {
if(eater.getScore()%400==0)
return true;
}
private void increaseDifficulty(){
world.increaseDifficulty();
}
and in your program:
if(isScoreEnough())
increaseDifficulty();
You are setting difficulty level in the function. Depending on score you increase/decrease the difficulty level. That is perfectly fine - as long as the function does the job of determining and setting difficulty level only. Function name has scope for improvement:
private void setDifficultyLevel() {
if (eater.getScore()%400 == 0) {
world.increaseDifficulty();
}
}
Obviously you won't find much code without any if statement, programs have to make decisions based on state, input or whatever. Avoiding if statements at all is also not the key message of "Clean Code". What you should avoid are for example endless if-else constructs if one class does the work of three logical cases - you should have three classes then.
Your method is already pretty short, but if you want to apply one of Martin's principles (a good name spares comments etc.) even further you could use something like:
if (scoreRequiresIncreaseInDifficulty()) {
increaseDifficulaty();
}
and then implement the two methods. However, I don't think this makes much sense in your case unless you will need the same calls multiple times and want to maintain the flexibility to change when the score requires a more difficult game in a single place.
You want to increase difficulty every 400 score points gathered by the eater, right?
So, you would need the eater to allow subscribing for score changes:
interface Eater {
void addScoreChangedListener(ScoreChangedListener listener);
}
interface ScoreChangedListener {
public void onScoreChanged(Score previousScore, Score newScore);
}
The implementation of Eater would hold a list of all score change listeners and invoke the onScoreChanged on each of them when the score changes. See the Observer pattern.
And then when bootstrapping your game:
Eater eater = ....
eater.addScorechangedListener(new DifficultyAdjustScoreChangeListener());
And you are done!
The implementation of DifficultyAdjustScoreChangeListener need to have the if, though, but that's ok:
class DifficultyAdjustScoreChangeListener implements ScoreChangedListener {
public void onScoreChanged(Score previousScore, Score newScore) {
if (newScore.value() % 400 == 0) {
world.increaseDifficulty();
}
}
}

How to comment code that has a complicated explanation [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
I have a if (a > b) { .. in my code, which I would like to explain why is there to someone looking at the code.
The reason for it being there is quite complicated and cannot be explained well without the use of images.
The question is, how to best document this to a programmer looking at the code? Is there a generally accepted way?
Should I create a comment in the code saying "See explanation XX", and then create a document somewhere containing XX?
Much of this will depend on what you are coding and the size of your project, but generally I would say that you should comment this particular conditional with an explanation only for what if (a > b) { .. is checking for, and why.
When you get to the content inside the if condition, explain that. Broader explanations as to the purpose of the function itself and its objectives should generally be in the declaration, although you can also add descriptions to the definition (I prefer to avoid this generally, although I sometimes describe the method in further detail on top of the definition, where it would simply clutter the declaration of the class).
For example,
class A
{
// this method performs operations on x or y as appropriate, given the input conditions of
// a and b. Its purpose is ... and in order to achieve this it does ...
void Method(int a, int b);
};
// elsewhere in a .cpp file
// Note that this method uses method abc rather than method cde in order to achieve
// such and such more efficiently (description here is more technical than in the
// declaration, and might focus on more specific issues while still remaining
// applicable to the function as a whole, and should therefore not be in the body)
void A::Method(int a, int b)
{
// check to see whether or not a > b in order to decide whether to operate on x or on y
if (a > b)
{
// a is greater than b, and therefore we need to operate on x because...
}
else
{
// a is not greater than b, therefore we need to operate on y because...
}
}
I find that by structuring my comments to address the reason why specific sections of code are the way they are, the reader is able to "follow the story" that the code is telling as she reads through it.
If it is absolutely impossible to describe what the if section is doing without a broader explanation, then by all means add a paragraph or two. There is nothing wrong with long comments as long as they are well placed and address the specific purpose of the following lines of code. You shouldn't need to add for more information, see function header because that should already be implied.
You can add broader descriptions to the enclosing function, method, or scope, but comments should always address the piece of code they are referring to as succinctly as possible. After all, if the reader wanted to know what the whole function was doing, she'd look at the declaration. The reason she's looking at the definition of the function is because she wants to know what the components of the function are doing, and so the comments should address just that.

Refactoring code that imports a csv file that has headers [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I'm trying to refactor some code that reads a csv file. The first character in every line of the file indicates the type of record: H = Header, I = Information, and D = Data. Each record type has a fixed and different number of fields. My program uses FasterCSV to read a line from the file and then uses a case statement to determine how to process the line.
record_type = new_record[:record_type]
case record_type
when "H"
factory_build_H_record(new_record)
when "I"
factory_build_I_record(new_record)
when "D"
factory_build_e_record(new_record)
end
For refactoring, I'm trying to follow Sandi Metz' blog post on the use of case statements in OO programming and eliminate case statements. My inclination is that I need to create some classes that represent the three record types and then define some methods like process_record. I'm not sure how I should go about to create the classes. Any help would be appreciated.
The blog post you linked is specifically about using case statements to tell what kind of object something is. That is often a symptom of bad design, and that is the point she is making.
The way you are using a case statement is much more acceptable. In fact, no matter how you restructure this, you are going to be doing some kind of test (case, or if, or otherwise) against that column in order to determine the correct behavior for the row.
If your code later on has to try to tell those objects apart by their class (using a case statement or if), then you are violating the spirit of the blog article.
The solution, is to create objects that have the same interface (have the same methods, used in the same way. The behavior of those methods is different internally in each class to do the right thing for that object.
Hope this helps clear up the concept for you :)
Case statements are not bad for small and fairly static conditional cases like the one you have.
If (theoretically) you anticipated having more record types in the future (besides H, I, and D), then the problem with the case statement is that it might cause you to start violating the Open-Closed Principle.
In this case, you might create a set of RecordType classes that is something along these lines:
class HeaderRecordType
def is_mine(record)
...
end
def build(record)
...
end
end
class DataRecordType
...
end
class SomeNewRecordType
...
end
Then, instead of the case, you would just iterate over a list of all the RecordType's (or use a Chain of Responsibility) and ask each one is_mine(record). As soon as one of them says yes, then you stop looking and call build.

How To write comments [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
Hi I have like 50 pages of code that I should write comments to it ...
Can you guys show me the best way. I meant I need you to write me a sample ...
the comments should contain nearly everything (classes, constructors, attribute, methods, events, functions)
Don't comment what is obvious like
//The width of the line is 2
lineWidth = 2;
or
//Clones the snapshot
tempDraw = (Bitmap)snapshot.Clone();
There might be a good idea to explain WHY a certain code line is there. For example explain why
panel1.Invalidate();
Needs to be invalidated.
The basic idea is: add extra information with comments and use them for explanations, don't create redundancy and duplication.
EDIT:
You might also want to explain why each item in the toolbar needs to be unchecked here:
private void toolStripButton1_Click(object sender, EventArgs e)
{
foreach (ToolStripButton btn in toolStrip1.Items)
{
btn.Checked = false;
}
...
}
because is not obvious from the name of the event handler which button is clicked in order to understand why all buttons are unchecked.
A good comment would be something like:
private void toolStripButton1_Click(object sender, EventArgs e)
{
//Deselect all previously applied filters because the user clicked "disable all",
//which removes the effects of all filters and we want to show this the the user
foreach (ToolStripButton btn in toolStrip1.Items)
{
btn.Checked = false;
}
...
}
Good comments will document intent, not function.
It's largely useless to comment assignments with "assign x to y" or similar.
It's much better to comment the code with a higher-level view of what the code aims to ultimately achieve, with pre- and post- conditions. You need to comment on (say) peculiar implementations or checks that are necessary yet counter-intuitive, and possibly reference specification documents etc.
If you've got to comment 50 pages of code, I suspect you're doing this at the wrong stage of your project. I tend to comment a class or method's intent with pre/post conditions prior to writing it. It's a form of mini-specification.
I recommend you to use XML comments in visual studio. Doing that you also can automatically generate documentation for your code, also other developers can see which method does what through intellisense.
http://www.winnershtriangle.com/w/Articles.XMLCommentsInCSharp.asp
http://msdn.microsoft.com/en-us/magazine/cc302121.aspx
You should not spend time writing documentation now, you should refactor this code. The design is not correct from a class-structure perspective. Your code should be structured as much as possible so that a class has a single responsibility it tries to achieve, and have a corresponding name.
Your Form1 (bad name, b.t.w.) does too much. It should ask other objects to help it. You might want to introduce a Tool class, which knows which label text and cursor are appropriate. You might want to use inheritance for the different shapes to do the drawing in a shape-specific way. In that way, your Form1 only has to delegate to the current tool.
With better structure you can reduce the amount of documentation you have to write.
You might want to look up CRC-cards.
I actually just wrote a blog post on this subject. Bear in mind that it is 100% possible (but perhaps not preferable) for code to contain no comments and be perfectly readable.

Resources