To properly answer your question I would focus firstly on why you want differentiated test sets, and not carry all the eggs in one basket. After that I'll focus on using factories or not, before a little summary at the end. (Sorry in advance, for the somewhat lengthy response.)
Some Test Levels
Different test sets could and should have various focus and coverage area, which again will reveal various kind of errors in your code.
Here is a list of various test areas (with my personal and simplified description):
- Unit Tests – Test the smallest unit of functionality, like a given method/feature. Should not rely on other components. Often mocking other components is used to avoid dependencies.
- Functional Tests – Checks specific features in a limited environment, but wider than in the Unit tests. Focus on getting the correct result of a function, with proper error handling, and so on.
- Integration Tests – Test the combination of units of code. This could be classes, and smaller sequences of code usage. Includes more of the actual environment the software is running within
- System Tests or Acceptance Tests – Tests within a "production" environment emulating full scale system operations. Depending on system, various tools could be used to emulate the usage of the entire software system.
For a similar description (and most likely better descritption) of various test levels, see https://en.wikipedia.org/wiki/Portal:Software_testing#Topics second line on "Test levels".
Note that the lower test levels here has a higher cost in terms of resource use, and time usage. As such, one tends to automate the upper levels and execute those rather often, whilst the lower levels are executed closer to delivery of the software.
Related to Your Question
The point I'm trying to make is that your question needs to take into account at what level you're testing. For a Unit Test I would test the function of Reset()
, but for Integration Test I wouldn't test that particular function but more the overall function of your class.
Whether this violates the SOLID principles or not, is yet again dependent on your focus of testing. To properly unit test the details of a class, maybe some of the SOLID principles will be violated internally to the test framework, but in the integration test they should not be violated.
For example, in multiple larger projects the firm I've worked for has used the Friend Assemblies (C# and Visual Basic from Microsoft. This allows an Unit Test project to access the inner workings of a befriended assembly/project. Aka it allows the Unit Test to access the internal
members of the class.
This could be argued that it violates the Open/closed principle and the Liskov substitution principle, but the benefits usually far exceeds the limitations. It does however imply that if you change internal details like from using an Array to using an Simple Linked list, this will require recoding of the Unit Tests (but not the Functional or Integration tests).
Factory or not
One of the advantage of using a Factory approach like you do in Option 2, is that allows for abstraction on a different level. This could be in accordance with the Single responsibility principle and the Dependency inversion principle of SOLID.
It could also shift the test responsibilities over to the factory implementation instead of relying on testing the inner working of your current class. Using a factory could also allow for different integrations at the switch of configuration.
This of course comes at a cost of introducing another layer in your software, and hiding some functionality within the factory. Then again, it also forces you to conform to some standard handling of the data structure (or whatever you're using the factory for), as you only can access it through its public interface).
Related to Your Code
Depending on the size of the Cells[,]
structure using a factory to instantiate it would allow for specialization of it depending on size, speed or other important criteria.
Imagining a scenario where this structure holds a large amount of Cells
the actual implementation of the structure could change from being held within memory, to accessing databases, using files, using a neural network (if going to the extreme). And done correctly, your code wouldn't know the difference at all. That could be a rather neat feature.
If however, you're always talking about a really small structure, this could be an awful waste of resources.
Regarding your statement: "although in that case, there's no way to test it", well, that is wrong. By introducing a factory implementation, you reduce the Unit Tests on this class, but should introduce another Unit Test of the factory class. It would also introduce a Functionality or Integration Test on the combination.
To Summarize
Firstly, I would strongly advice you to look into Friendly Assemblies, as they provide a great tool for unit testing the internals of your code, without exposing functionality outside of your library.
Secondly, using factories or other means, to extract the internal implementation of the Cells[,]
, depends on various factors like speed, size, complexity, connectivity, and so on. In some cases, it will really help your implementation.
Thirdly, if you choose to factorize Cells[,]
, it can still be tested, and in some cases it can even enhance your overall system as you have restrained access to the Cells structure, and thusly limited wrong usage of the structure. And the tests can ensure that the public interfaces does what it should, and nothing else.
ICellFactory
. \$\endgroup\$