TDD para un algoritmo aleatorio

I've got a problem with tests. There is an algorithm for some fancy procedure. This algorithm takes a random number from a range [-999,999; +999,999], treats it like an id number from a table and performs some random operations in the database. This means that I need to call the method huge number of times to be sure that the random number distribution is correct.

I wanted to make all the code using TDD (just to learn it a little bit more).

The problem is that I don't know how to test the algorithm with TDD principle in mind.

According to TDD, no code should be run without writing a test first.

One of the solutions I think of is to have a dummy method in the main class called debug(text). This method will do nothing in the production code. However then I'd make a subclass with this method being overloaded and this time it would store some interesting information. This information could be later used by test to find out if the function works properly. The database connection will be mocked and will do nothing.

Another solution would be to create a mocked database connection which would store the interesting information, used later in tests. However it would be so huge amount of work to create such a connection that I don't think is worth spending time on it.

There will be integration tests later to check if the database is changed properly. But integration tests are not part of TDD.

Have I got into a place where TDD fails and is useless or too hard to use?

preguntado el 28 de agosto de 12 a las 15:08

3 Respuestas

Lo es su random number function?

Es: The random number generator should be tested outside of anything that uses it.

No es: You shouldn't be testing it at all, unless you really have a need to validate how random it is. IMO not a great ROI, but it depends entirely on your actual needs.

The DB functionality should assume the RNG is actually R, and should be tested por separado from the RNG–during testing you may not want to use the RNG. At the least, you may want to seed the RNG to make the tests repeatable–this may make correctness more difficult to verify.

Respondido 29 ago 12, 15:08

This is the problem number one :) In fact the algorithm uses two random number generators. One has uniform distribution, the other has uniform or not uniform depending on some configuration params. The generators are tested according to TDD with their own tests. However the class which is using them should be tested as well. - Szymon Lipinski

@SzymonGuz And that's fine, but they no need to be run a "huge number of times" to test distribution, because the RNGs are already tested. - David Newton

@SzymonGuz: what would the test of "the class which is using them" be trying to prove? If it is to prove that the RNGs work, then this is a redundant test. If it's to prove that the class itself works, then the RNG functionality shouldn't matter. - dan puzey

Right, this is a really good idea. I didn't think of testing the class using some mocked RNG returning always the same number. This way the tests of the class will be easy. However I need to test the class with the real RNGs to ensure that the class functionality works correctly. This mean I need to test from outside if the class uses proper RNG functions. - Szymon Lipinski

@SzymonGuz You just said you wanted to make sure the class is using the right RNG class, but you don't want to test it. You do. Lo que tu no want to do is re -test the RNG class. It sounds like you're conflating testing concerns; you're trying to validate the class under test is passing the right params to the RNG. That's a completely different thing than testing the RNG, it's testing whatever configuration mechanism you're using--which should also be tested somewhere else. - David Newton

Here are some assumptions for your code-base:

1) The call to the stored procedure is in data access code.

2) The data access code implements an interface.

3) The business logic you want to write with TDD can inject the data access code into it's constructor.

If that is the case, you can use a mocking framework to mock your data acess code. The actual stored procedure isn't called.

The new code can be developed using TDD.

Respondido 28 ago 12, 15:08

You should consider your design again. Unit Testing (trough TDD or by adding tests later) should test each class in complete isolation.

In your case, you have a few distinct functions:

  • A random number generator
  • A lookup in database
  • Some code that's run against the database

Each of these can be tested in complete isolation of each other by using design patterns such as Inyección de dependencia y Burlón.

Unit Tests should not depend on random behavior. That way they will become brittle and hard to maintain.

So in your case, you would test your random number generator by running it a significant amount of times and checking if the results are in the expected range. That test should succeed each time it's run, no matter the time of day.

For the database part, I would create an interface that would hide your database code (have a look at the Patrón de repositorio). In your unit tests you can mock this interface and check if the correct functions are called with the right arguments. In your integration tests, you can use the real Repository implementation.

The second test then checks if your database lookup is working.Check if the 'random number' that's passed is used correctly to call the correct methods on your database mock.

The third test would check if the correct code is executed against your database mock.

A couple of months ago I wrote an article for .NET magazine about Unit Testing and some best practices. Maybe it can help: Pruebas unitarias, ¿infierno o cielo?

Respondido 28 ago 12, 15:08

Second test: I don't pass the random number. The class has the RNGs, and uses numbers from that. I just need to ensure that the class uses RNGs returning number within proper range with a proper distribution. - Szymon Lipinski

@SzymonGuz Then I'd suggest your class isn't separated properly. It doesn't make any sense to run a DB test a "huge number of times" to make sure it's using "properly random numbers" from something you've already tested to generate property random numbers. - David Newton

@SzymonGuz then that's the problem with your design. You should use Dependency Injection to inject the RNG into your class. In your test you can then mock it. Test all classes in isolation remains the key success to proper unit testing - Wouter de Kort

Well, I think that's the point. So, I'd make a DAO with database operations. I'd make a MockDAO where I could test if the main class is using it as it should, so I could test the main class behaviour without changing that. Next step would be a simple integration test with real DAO class where it could make a couple of operations and I'd just check if the database changed as it should. Does it make sense? - Szymon Lipinski

Yes! :) Your integration test will probably be quite small. You have already tested all possible paths trough your code (including error conditions) so in your integration test you only have to test if all parts are assembled correctly. To make mocking easier, you can use an interface for your DAO, - Wouter de Kort

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.