0

I currently develop a fairly large application which, amongst other things, calculates mathematical problems. Calculations are made in classes, let’s call them A and B, which have some random final attributes and a (unique) final int id as instance variables.

I want to be able to run the program in a test mode, where in each execution, the random variables are exactly the same and allow me, for example, to compare the results with hand-made calculations, using Junit tests. Obviously, I don't want to re-calculate my sample solutions after every code change (for example, if an earlier access to the prng is inserted which would shift all random numbers).

Note that I currently use Java.util.Random as prng, but am open to suggestions.

The question is now: How should I structure the instantiation and accessing of the prng? To point out the problem, note that following would be very bad approaches:

  • I cannot make it a singleton and access from everywhere, as then, the random numbers within the classes would depend on the order of instantiation.
  • I obviously cannot instantiate a new Random(seed) with a hardcoded seed, as the numbers would not be different if the programm is run multiple times (outside of test mode).

I came up with the following solution (largely based on this answer), but not identical, as the programming language and test seed generation is different.

public class PRNGeneratorGenerator{ //Make class a singleton private PRNGeneratorGenerator instance; private PRNGeneratorGenerator() {} public PRNGeneratorGenerator getInstance(){ if (instance == null) instance = new PRNGeneratorGenerator(); return instance; } //RandomNumberGenerator-Methods and Attributes private boolean isTestMode = false; private Random seedRng = new Random(); public void setTestMode(boolean testMode){ isTestMode = testMode; } public Random getPseudorandomNumberGenerator(long testSeed){ if(isTestMode) return new Random(testSeed); return new Random(seedRng.nextLong()); } } 

The classes (named A and B above) with the final random numbers would then look as follows:

public class A{ private final static long CLASS_SEED = 872349; private final int randomNumberOne; private final int randomNumberTwo; private final int id; public A(int id){ this.id = id; long testSeed = CLASS_SEED + id; Random rnd = PRNGeneratorGenerator.getInstance().getPseudorandomNumberGenerator(testSeed); randomNumberOne = rnd.nextInt(); randomNumberTwo = rnd.nextInt(); } } 

In the test mode, I would call PRNGeneratorGenerator.getInstance().setTestMode(true) before starting any instantiations.

Is this a good way to solve my problem in java or are there any downsides of this approach? I have read a number of similiar questions, but have found no equivalent ones which answers my question. Thanks in advance for your answer.

3
  • 5
    if (testMode) is almost always a smell! Dependency injection is probably the answer here (i.e. you supply PRNGs to your classes via constructor parameters). Commented Jan 3, 2015 at 17:00
  • Just another option, use any of the FITness unit test frameworks, you can generate test data in tables together with the results. You need to keep them anyway, so it does not help to generate random numbers with a fixed seed. Commented Jan 3, 2015 at 18:47
  • @OliverCharlesworth: I didn't like it either; that's why felt I needed to post it on SO to collect opinions. ;) Dependency Incetion looks like a very good solution and I will use it. Thanks for your comment! Do you want to post an answer which I can then accept and give you credit for it or should I post an answer myself? Commented Jan 9, 2015 at 13:33

1 Answer 1

2

I'd suggest making a class "PRNGeneratorGeneratorTest" that creates its own local instance of PRNGeneratorGenerator for testing. It's a bad idea to shuffle test & production code for lots of reasons: e.g. it's confusing, it's easy to leave something in the code from a test that flips the mode on when it's not wanted, etc.. A test class has other benefits anyway, such as giving you the ability to do all your tests simultaneously in 1 build with the results auto-displayed & saved

If you don't want to make a test class, at the very least divide getPseudorandomNumberGenerator in 2 (i.e. make a test method):

public Random getPseudorandomNumberGenerator(){ return new Random(seedRng.nextLong()); } public Random getPseudorandomNumberGeneratorTest(long testSeed){ return new Random(testSeed); } 

..it's not great practice, but it's significantly better than shuffling up your test and production code.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.