-1

I have the following problem. I have a method which takes two other custom objects to work. This objects are required by the method because they have the information that the method requires to perform its operations. Normally, you could mock the objects and just hardcode the API of this objects.

The problem here is that the methods that this objects return is hard/impossible to replicate by hardcoding because it includes images and arrays with several parameters. This complicated data is used by the other object to perform some calculations, and this method is suposed to format the output and make some graphical analysis with the output data.

I have checked Should each unit test be able to be run independently of other tests? on how to do this, looking for a way of testing this method which doesn't include to construct the two objects needed inside the test but I have found nothing that solves this problem.

My question is: Should I just use my objects constructors to test this method or there's another cleaner way to test this method?

Pd: I'm working in python, but I hope to find a language-independent solution

15
  • 1
    Can you use the existing data to stub the methods for testing? – Robert Harvey Mar 18 at 12:43
  • 1
    Sounds to me like you might have a level of detail issue. Either what you are looking for are module level tests, in which case sure load them up as real classes and pass them in, and then check the output. Or you really should break this class into smaller pieces so that you can independently verify orchestration logic, and the various transforms in simpler terms. You can still leave the class itself around as a facade/orchestrator, but it should be much easier to unit test. – Kain0_0 Mar 18 at 12:52
  • 1
    I am pretty sure this question is unanswerable from this 30000 feet view presented in this question. One has to look at the real code (codereview.stackexchange.com is the place for this). – Doc Brown Mar 18 at 12:56
  • 1
    What Kain is saying in broad brush strokes is that your class needs to be redesigned so that it is more easily testable, perhaps by breaking it into several smaller classes. – Robert Harvey Mar 18 at 12:59
  • 1
    If you require a large data set for a test then by all means load it from the hard drive. Just be sure to separate this slow test from the faster hard coded tests so it wont slow them down. Nothing mentioned here is impossible to test. – candied_orange Mar 18 at 13:43
2

SRP

The problem here is that the methods that this objects return is hard/impossible to replicate by hardcoding because it includes images and arrays with several parameters. This complicated data is used by the other object to perform some calculations, and this method is suposed to format the output and make some graphical analysis with the output data.

This sounds like a lot of work/responsibility for something that you call a "unit". For example, it makes no sense for both image processing and graph outputting to be tested as a single unit. Those are two completely independent behaviors.

I support RobertHarvey and Kain's suggestion of breaking this down in smaller chunks. It's going to dramatically improve your testability and reduce the complexity of your inputs and outputs.

Unit testing with large input data

Let's assume you've broken down your logic, and you're trying to unit test the image processing logic. For the sake of example, let's say the logic counts how many pixels of each color there are in the image.
This is an appropriate unit test, but the image is still a non-trivial data set to construct out of nowhere. So how do you define this test data?

The problem here is that the methods that this objects return is hard/impossible to replicate by hardcoding because it includes images and arrays with several parameters.

Unit tests don't limit you to having to hardcode inputs in the test method itself. What is important is that the values of the input data are considered a fixed given, i.e. the test presumes a specific input. For simple data, that's trivially done by hardcoding it, but that's not feasible for larger data sets.

looking for a way of testing this method which doesn't include to construct the two objects needed inside the test

The simple solution here is to include an actual image in your test project, and simply read that image's file content. If I know that myImage.jpg contains 110 black pixels and 890 white pixels, I can write a unit test that asserts these values are the output of the image processing logic.

Just like how you decide an arbitrary value when hardcoding something, you can use an arbitrarily decided image file.

It doesn't have to be a literal image file. It could also be a hardcoded string that you keep in a separate file, e.g. a TestImages class with static string properties containing the base64 file content.

I would try to work with image files where possible, simply because it's easier for a test writer to understand which image they're using in their test. The minor performance hit from reading the file is negligible since unit tests aren't written to be performant to begin with.

Note: The same answer/principle applies to any unwieldy large data sets, not just images. Simply define them elsewhere and refer to them in your unit test. For example, in a unit test for an import script that had to process a massive XML file, we simply had a collection of XML files with particular representative data cases which we used.

5
  • Actually I'm using a small dataset to test other parts of the codebase. The problem is that the dataset by itself is not enough, it has to be processed because it has to be normalized and filters applied (Like removing NAN values and so). – Jorge Verdeguer Gómez Mar 18 at 14:20
  • @JorgeVerdeguerGómez: There's no need to test all those things at once. If you write a test that confirms NAN values are filtered, then your other test can work with a data set that doesn't contain any NAN values. Same goes for normalization. This is why people are suggesting you break down your logic (and thus tests) into more easily digestible chunks. You're trying to take too much on all at once, which is contributing to the complexity that is now your roadblock. – Flater Mar 18 at 14:40
  • As I said, I have tests that are already testing that. The two objects that I'm passing to this method are already tested. It is not responsability of this method to check if the data is correct. However, it is its responsability to be able to operate on clean data, because it is what it will receive from the other object. – Jorge Verdeguer Gómez Mar 18 at 14:47
  • 2
    @JorgeVerdeguerGómez, If you are confident that those input objects are correct (and if they aren't, a test related to them will show that), then by all means use those objects in your test. – Bart van Ingen Schenau Mar 18 at 14:53
  • 1
    @JorgeVerdeguerGómez That the data gets cleaned vs what you do with the clean data afterwards are two separate responsibilities, which should be tested separately. The latter test can be provided clean data without needing to sanitize it any further. Unit tests focus on the specific unit. The unit that uses the clean data therefore should be supplied with clean data in its test. – Flater Mar 18 at 16:34
0

"Should I just use my objects constructors to test this method or there's another cleaner way to test this method?"

Yes to both.

Yes. It sounds like you have a complex case and simply using your existing objects to test the third is pragmatic. You can argue about whether its a unit or integration test later.

Yes. The cleaner way would be to mock the parameter objects with some classes which have the same interface but are populated by some static data.

In order to achieve this second cleaner method I would suggest

  1. Make sure you are passing interfaces rather than concrete objects
  2. Make a serialiser for your objects that can write them or the results of any method calls to a file.
  3. Make a simple object with no logic which matches the same interface
  4. Populate it from a file created by the serialiser of a real object.

Obviously this is more work. But its good work. If you find that you can't do it due to some complexity of your objects then it suggests that there may be a problem with your overall design.

Mostly when people object to these "best practice" style answers it's not because it's impossible, it's because it questions their design decisions. Sometimes its because they have an exceptional case. But if you have an exceptional case there's no way for us to know unless you post all your code and the answer will be specific to your case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.