tags:

views:

577

answers:

8

I'm just getting into unit testing, and have written some short tests to check if function called isPrime() works correctly.

I've got a test that checks that the function works, and have some test data in the form of some numbers and the expected return value.

How many should I test? How do I decide on which to test? What's the best-practices here?

One approach would be to generate 1000 primes, then loop through them all, another would be to just select 4 or 5 and test them. What's the correct thing to do?

A: 

Ask yourself. What EXACTLY do I want to test. And test the most important. Test to make sure it basically does what you are expecting it to do in the expected cases.

Testing all those nulls and edge-cases - I - don't think is real, too time consuming and someone needs to maintain that later! And...your test code should be simple enough so that you do not need to test your test code!

If you want to check that your function correctly applied the algorithm and works in general - probably will be enough some primes.

If you want prove that the method for finding primes is CORRECT - 100000 primes will not be enough :) But you don't want to test the latter (probably)...

Only you know what you want to test! :)

PS I thnik using loops in unit tests is not always wrong but I would think twice before doing that. Test code should be VERY simple. What if something goes wrong and there is a bug in your test???? :) However, you should try to avoid test code duplication as regular code duplication. Someone has to maintain test code.

PEOPLE PLEASE COMMENT WHY YOU THINK THIS WAS WORTH TWO DOWNVOTES :)

badbadboy
I didn't vote you down, but I'd guess that people disagreed with what you said about it not being necessary to test edge cases. Those are exactly the cases that need the most testing. Other than that, I thought you had good points, esp. about bugs in the test.
Clayton
@Clayton - Ok, but I don't understand this "edge" cases...I mean...I know, this is what you read in your first unit testing book, but what is the "edge case" for this example? Can anyone tell me?Making sure that 2^32 works? Or that -3 doesn't? Okay, now tell me, what is 2^32?
badbadboy
@Clayton - What do I know about 2^32 in my DOMAIN? If I know this is specific important case for me - I'll test it, but why calling it an edge test? If your program crashes because of bad user input - that's error in your GUI handling, not in your domain class.However, I agree that
badbadboy
if you display an error message about bad user input - then it is important to throw the right exception...but in general it is more important to test how the program WORKS and not how it DOES NOT work...I think.Maybe I am missing point here...can ANYONE give an example of good edge case here?
badbadboy
This isn't worth 2 downvotes. I agree - testing all edge cases may or may not be necessary (i.e., don't test a proven algorithm, just test the implementation).I vote up.
Cybis
@badboy: An "edge case" is one that might hit a limitation of the hardware, OS, or algorithm. An example of that is one where a composite number has a prime factor very close to its square root. In that case, you might get bad results because of round-off in the sqrt function.
Clayton
@badboy: The round-off error in sqrt() is the only "edge case" I can think of for this, and that would only apply if you used the algorithm I had in mind. Other algs will have other limits. For this funct, maybe there aren't many edge cases. If this were a floating-point funct you'd have lots.
Clayton
@badboy: Other edge cases for isPrime() would be 0 and 1. Those are not prime by definition. If they're not handled as special cases, the funct might think they are prime.
Clayton
@Clayton - okay, I agree, if he needs detailed testing of algorithm - he needs to test those "prime factor very close to its square root". But as you mentioned - that's alg specific and he is probably not testing alg but HIS implementation and not common problems implementing this alg on PC...
badbadboy
@Clayton - about 0 and 1 - I would agree if it is important for his domain only. Otherwise he might spend time researching definitions to make sure his function is "correct", however I bet his program functions correct both treating 0 and 1 as primes and not.
badbadboy
@I would substitute "check edge cases" with "check special cases in your domain".
badbadboy
+6  A: 

You'd want to check edge cases. How big a prime number is your method supposed to be able to handle? This will depend on what representation (type) you used. If you're only interested in small (really relative term when used in number theory) primes, you're probably using int or long. Test a handful of the biggest primes you can in the representation you've chosen. Make sure you check some non-prime numbers too. (These are much easier to verify independently.)

Naturally, you'll also want to test a few small numbers (primes and non-primes) and a few in the middle of the range. A handful of each should be plenty. Also make sure you throw an exception (or return an error code, whichever is your preference) for numbers that are out of range of your valid inputs.

Bill the Lizard
why would you test edge cases first? What about all these numerous and useless checks for null and other stuff making together 80% of your test code, and then it is time to go home before you even wrote the "real test"? He wants the functino to work for "normal" numbers. How would you test edges?
badbadboy
Let's say function works for 125 and crashes for 12234235345. Would you spend time making sure it throws ArgumentException instead of crashing? However, I do agree - it is nice to test edge cases if you are done with all the other stuff and feel it is useful.
badbadboy
@badbadboy The reason for testing the edge cases is very often that's where the bugs lie - and the purpose of testing is to expose the bugs.
ChrisN
Of course you test all that other stuff too. The question seemed to be specifically about what data to test.
Bill the Lizard
@ChrisN: Yes, this we all kinda know. Could you make an example of useful edge case testing here?
badbadboy
I'm speaking generally. Bill's answer covers the specifics in this case.
ChrisN
How about negative numbers for an edge case? Is -3 even prime?
Jeremy Frey
No, negative numbers aren't prime. They're divisible by a number other than 1 and themselves (-1).
Bill the Lizard
2 is the lowest prime number, so 0 and 1 are not prime by definition. This might also be true of negatives, but I think my last comment still holds up.
Bill the Lizard
According to ring theory, -3 IS technically prime, because it has no factorization pq where p and q are not units (numbers with multiplicative inverses, i.e. +1 and -1). However, for most reasonable purposes, negative numbers should not be considered prime.
Adam Rosenfield
@Bill the Lizard - this is my very point. Instead of just developing, you will seat and think "hmm...is -3 a prime?" I DON'T CARE! This is not what I am going to use it for. And in most cases - making sure you will get ArgumentException instead of DivisionByZero or NullReference - is not worth it!
badbadboy
@Bill the Lizard - maybe I am missing point here...can anyone show me good edge cases here and expected behavior?
badbadboy
You aren't going to be the only one to use your code. You need to test on the entire range of expected inputs to make sure it works. You also need to test just outside the expected range to make sure it gives the right error. This way you can tell the users what to expect when they give bad input
Bill the Lizard
@Bill the Lizard - I would agree with you on proper error messages if you're writing the library. Then it's part of spec. If it's not - I don't see MUCH point testing how exactly the program doesn't work. At least, I know cases when people spend 90% time testing useless edge cases, ending up with
badbadboy
ending up with not doing tests at all really. Don't forget you have to maintain all the test code as well. And if it is almost useless - is it worth it?
badbadboy
A: 

A few questions, the answers may inform your decision:

  • How important is the correct functioning of this code?
  • Is the implementation of this code likely to be changed in the future? (if so, test more to support the future change)
  • Is the public contract of this code likely to change in the future? (if so, test less - to reduce the amount of throw-away test code)
  • How is the code coverage, are all branches visited?
  • Even if the code doesn't branch, are boundary considerations tested?
  • Do the tests run quickly?

Edit: Hmm, so to advise in your specific scenario. Since you started writing unit tests yesterday, you might not have the experience to decide among all these factors. Let me help you:

  • This code is probably not too important (no one dies, no one goes to war, no one is sued), so a smattering of tests will be fine.
  • The implementation probably won't change (prime number techniques are well known), so we don't need tests to support this. If the implementation does change, it's probably due to an observed failing value. That can be added as a new test at the time of change.
  • The public contract of this won't change.
  • Get 100% code coverage on this. There's no reason to write code that a test doesn't visit in this circumstance. You should be able to do this with a small number of tests.
  • If you care what the code does when zero is called, test that.
  • The small number of tests should run quickly. This will allow them to be run frequently (both by developers and by automation).

I would test 1, 2, 3, 21, 23, a "large" prime (5 digits), a "large" non-prime and 0 if you care what this does with 0.

David B
this seemed to me a bit misleading advices for the guy who started unit testing today or yesterday.
badbadboy
Perhaps not the best answer IMO, but certainly not worth a downvote. Experienced testers on real-world projects do have to consider these issues, so I'll vote it back up.
Cybis
It's always a struggle to answer the general or the specific question. The downvotes were probably deserved and thanks for encouraging me to better the answer.
David B
@Cybis - downvote is not death... :) I just got two downvotes for this very question, now I will think, maybe I am missing something here?...
badbadboy
+1  A: 

in general, test as many cases as you need to feel comfortable/confident

also in general, test the base/zero case, the maximum case, and at least one median/middle case

also test expected-exception cases, if applicable

if you're unsure of your prime algorithm, then by all means test it with the first 1000 primes or so, to gain confidence

Steven A. Lowe
What do you mean by this?test the base/zero case, the maximum case, and at least one median/middle
badbadboy
@badbadboy: suppose your function takes an input value X with range 0..N; the base/zero case would be X=0, the maximum would be X=N, and the median would be X=N/2. I would also test -1 and N+1 to make sure they failed as expected
Steven A. Lowe
+1  A: 

"Beware of bugs. I have proven the above algorithm correct, but have not tested it yet."

Some people don't understand the above quote (paraphrase?), but it makes perfect sense when you think about it. Tests will never prove an algorithm correct, they only help to indicate whether you've coded it right. Write tests for mistakes you expect might appear and for boundary conditions to achieve good code coverage. Don't just be picking values out of the blue to see if they work, because that might lead to lots of tests which all test exactly the same thing.

For your example, just hand-select a few primes and non-primes to test specific conditions in the implementation.

Cybis
There's a really good but subtle point here, in that you want to take both the implementation and specification of your function into account when designing tests.
Mark Bessey
+5  A: 

I've also been informed that every time a bug is found, you should write a test to verify that it is fixed. It seems reasonable to me, anyway.

Lieutenant Frost
Excellent point. This keeps you from manually rediscovering the bug again when you change your code.
Bill the Lizard
This is in fact the single most useful kind of test for long-lived projects, in my experience. If a mistake is made once, it'll likely be made again later,
Mark Bessey
A: 

To be really sure, you're going to have to test them all. :-)

Seriously though, for this kind of function you're probably using an established and proven algorithm. The main thing you need to do is verify that your code correctly implements the algorithm.

The other thing is to make sure you understand the limits of your number representation, whatever that is. At the very least, this will put an upper limit on the size the number you can test. E.g., if you use a 32-bit unsigned int, you're never going to be able to test values larger than 4G. Possibly your limit will be lower than that, depending on the details of your implementation.

Just as an example of something that could go wrong with an implementation: A simple algorithm for testing primes is to try dividing the candidate by all known primes up to the square root of the candidate. The square root function will not necessarily give an exact result, so to be safe you should go a bit past that. How far past would depend on specifically how the square root function is implemented and how much it could be off.

Another note on testing: In addition to testing known primes to see if your function correctly identifies them as prime, also test known composite numbers to make sure you're not getting "false positives." To make sure you get that square root function thing right, pick some composite numbers that have a prime factor as close as possible to their square root.

Also, consider how you're going to "generate" your list of primes for testing. Can you trust that list to be correct? How were those numbers tested, and by whom?

You might consider coding two functions and testing them against each other. One could be a simple but slow algorithm that you can be more sure of having coded correctly, and the other a faster but more complex one that you really want to use in your app, but is more likely to have a coding mistake.

Clayton
A: 

"If it's worth building, it's worth testing"
"If it's not worth testing, why are you wasting your time working on it?"

I'm guessing you didn't subscribe to test first, where you write the test before you write the code?
Not to worry, I'm sure you are not alone.
As has been said above, test the edges, great place to start. Also must test bad cases, if you only test what you know works, you can be sure that a bad case will happen in production at the worst time.

Oh and "I've just found the last bug" - HA HA.

jsfain