
Do you like to cover code with tests? Do you like the pleasant warm feeling of security that comes with passing tests?
Well done!
Real professionals do not rely on chance, they
lay straws in advance and keep everything under control.
')
Want to inside, for the public interface, too, everything was covered in tests?
As always, there are several ways. There are better, there are easier. Go!
1. Making the method public
No private method - no problem. You can even write a comment to the method
Pros: very simple.
Minuses: the interface is littered, encapsulation suffers.
2. Making the method internal
That is, we change the access modifier from
private
to
internal
. The comment from the first method will also fit here. It turns out a method that is available from anywhere within the assembly.
And test it as? Simply. You need to add the
InternalsVisibleTo attribute to the assembly. This attribute will give the test assembly access to all internal methods and properties of the assembly under test.
Do not forget that the functioning of the
InternalsVisibleTo
attribute requires that both assemblies (testing and tested) be
signed with a strong name at the same time, or not signed at the same time.
Pros: simply enough, there remains control over who has access to the interiors of the assembly.
Minuses: the interface is still clogged, encapsulation still suffers, additional conditions appear (see above about signing), the attribute remains in the release build.
3. Making the method protected
Instead of the
private
modifier, the method should acquire the
protected
modifier. In the test build, you will need to make an inheritor from the class under test and - voila! - access to the method obtained.
Pros: simple enough, there is some control over who has access to the method.
Minuses: the interface is still clogged, encapsulation still suffers, the method is available to anyone who wishes to inherit it, a class with this method cannot be marked closed for inheritance (sealed).
4. Use PrivateObject
This class provides the
Visual Studio Unit Testing Framework , so if a project uses NUnit or something else, then this method will not work.
With
PrivateObject
everything is simple. There is a class for testing:
public class ClassToTest { private string field; private void PrintField() { Console.WriteLine(field); } }
There is a testing class:
[TestClass] public class TestClass { [TestMethod] public void TestPrivateMethod() { ClassToTest testedClass = new ClassToTest(); PrivateObject privateObject = new PrivateObject(testedClass); privateObject.SetField("field", "Don't panic"); privateObject.Invoke("PrintField"); } }
After executing the test in the console, the string will
Don't panic
. Always good advice, right?
Pros: you do not need to change the existing code, quite simply.
Disadvantages: applicable only to the Visual Studio Unit Testing Framework, when renaming fields and methods, the tests will start to fall.
5. Reflection will help us
The code will be longer than in the previous method, but you can live.
Again, there is a class for testing:
public class ClassToTest { private string field; private void PrintField() { Console.WriteLine(field); } }
In the test you need to write the following:
ClassToTest obj = new ClassToTest(); Type t = typeof(ClassToTest); FieldInfo f = t.GetField("field", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); f.SetValue(obj, "Don't panic"); t.InvokeMember("PrintField", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, obj, null);
Good advice should appear in the console again.
The above code can be used in any tests. Though for NUnit, at least for the Visual Studio Unit Testing Framework, at least for any other testing environment.
Pros: there is no need to change the existing code, quite simply, applicable to any testing environment.
Minuses: without the auxiliary class in the tests there will be kilometers of the repeating code, when renaming the fields and methods, the tests will start to fall.
Instead of conclusion
Now that you know how to test non-public methods, it's time to think: should we test them?
Arguments for":
- Non-public methods may contain complex logic that is worth checking.
- Sometimes using private methods in tests can reduce the amount of code needed to create test data.
- In general, you need to test everything. The more lines of code covered in tests, the better.
Arguments against":
- You need to test the behavior, not the implementation. So, you only need to test the class interface (only public methods and properties).
- Tests for private methods interfere with refactoring code, and refactoring code is no less important than testing.
- If the method is private, then it is not just that. This means that you don’t need to touch it.
Personally, I play for the team "against." I think that you only need to test public methods and properties.