Keyword for internal class tests #1448
Replies: 21 comments
-
A C# keyword isn't going to help with the CLR which is where accessibility is enforced. How would this be implemented? |
Beta Was this translation helpful? Give feedback.
-
Why do you think there are troubles with CLR? When you run tests the compiler should implicitly package all tests' code into an implicit nested class. A nested class can access all not public properties. No problem at all. |
Beta Was this translation helpful? Give feedback.
-
This sounds like an alias for |
Beta Was this translation helpful? Give feedback.
-
So you'd be testing an assembly that differs from the one you'll receive (or rather a compiler will generate) when publishing/debugging? That sounds like a horrible idea. A project results in an assembly, and CLR enforces that other (e.g. test) assemblies cannot access these members. You propose to either generate a "merged" assembly that inlines a class into your outer-test-class (therefore creating an assembly that has a completely different structure to the one normally generated), or you propose making something like a cross-assembly partial mechanism, which also completely breaks security. |
Beta Was this translation helpful? Give feedback.
-
Or maybe another option. Tests could not be packaged into a nested class and could be packaged into another dll. Then when you run tests a compiler should implicitly change an access modifiers of all members that are used in tests from not public to public. |
Beta Was this translation helpful? Give feedback.
-
Oh, so your goal is to nest the test code within the same assembly as a nested class. That would work for testing the internals/privates of the target class, although only of that class. I agree with @yaakov-h, you can accomplish this today with preprocessor directives. |
Beta Was this translation helpful? Give feedback.
-
It has a different structure but the difference is only that a test assembly contains one additional nested class. I don't think it should break something. Your code should work the same way for test-build and production-build. |
Beta Was this translation helpful? Give feedback.
-
I don't see it only as a way to test private/internal members, I also see it as a way of having test code as close as possible to the business code, which in my opinion is not such a bad idea :-) |
Beta Was this translation helpful? Give feedback.
-
Why so concerned with unit-testing internal/private methods? If they fail to do their job, then that should be apparent when running tests against the public API. |
Beta Was this translation helpful? Give feedback.
-
I strongly agree that the assembly being tested should be a binary identical to the one you ship. I would probably approach this by having two .csprojs in the same folder and putting all test code in *.tests.cs, with Having the code colocated is great. However, I dislike the idea of special visibility rules for this. InternalsVisibleTo is good enough if you really need it. /cc @patriksvensson re |
Beta Was this translation helpful? Give feedback.
-
There already is a way of testing them: via the public API. That is the only way you should ever test private and internal parts of your code. Attempting to test them directly leads to brittle tests that break all too easily when you change the internal works of your system. Further, if you can't test a private method via a public API, then that means that method is unreachable from the public API, so does nothing and so should be deleted as dead code. If you start testing private methods directly, you'd never discover dead dead parts of your code as everything would be being used: by tests. |
Beta Was this translation helpful? Give feedback.
-
To be fair, this is often not enough. There are rarely ways to mock away IO and other dependencies which unit tests ought not to involve, lest they become functional tests. That said, I agree with several respondents that a new keyword is likely not the proper solution. Not certain what the correct solution would be, but I'm fairly certain it's not a visibility keyword. |
Beta Was this translation helpful? Give feedback.
-
Yeah, in the past I've ended up with something like the following, which I feel is far from ideal.
Then a test would do something like:
Relies on InternalsVisibleTo("TestProject") to set the internal property to be mocked. It works as long as those are in their own assembly and so you can control who has access to internals. You quickly end up with effort > reward and I just don't bother writing unit tests for stuff that has lots of IO, reading registry etc... (I know DI should be solution, but sometimes you are writing something that a 3rd party will use and you can't expect them to inject in something to do fileIO, or reading registry etc) Also not sure what solution is. |
Beta Was this translation helpful? Give feedback.
-
public sealed class Delivery
{
private readonly IFileIoWrapper _ioHelper;
public Delivery(IFileIoWrapper ioHelper) => _ioHelper = ioHelper;
public void Drop(string location, string content)
{
_ioHelper.CreateFolderIfDoesNotExist(location); // fixes the thread safety issue
// with your check & action version
_ioHelper.WriteAllLines("file", content);
} There, fixed it for you. |
Beta Was this translation helpful? Give feedback.
-
What if this is a class that will be consumed by someone who can't / won't write an IFileIoWrapper. They expect to do:
And it just works. I would still like to be able to test it. There might be other dependencies like a registryReader to get the drop folder location and some thing to generate certain predictable filenames etc... That's when we've used the above pattern, things aimed at 3rd parties who neither need to know nor care about the fact that it does FileIO and wouldn't know how to write what it needs to operate. Other than that, i would agree that DI like you have thereis the way to go, it's not always feasible though. |
Beta Was this translation helpful? Give feedback.
-
Tough luck for them. 🙃 But really, if you're that concerned, then provide a standard implementation that'll defer to the filesystem as users expect (heck, you can have a parameterless ctor set that implementation automatically and say that is the default behavior). |
Beta Was this translation helpful? Give feedback.
-
Imo that is not realistic and it's not good design. |
Beta Was this translation helpful? Give feedback.
-
Why is this bad advice? Seems sensible to me. Plus, it means you don't have a brittle, unstatable, connection between you internal implementation details and your tests. |
Beta Was this translation helpful? Give feedback.
-
If you really want to do this, you can go with teh following approach (though i still think its inadvisable to be testing this stuff if the first place): YourLib: public class C
{
private static void SomethingPrivate() { }
internal static void SomethingPrivate_ForTestingPurposesOnly() => SomethingPrivate()
}
[InternalsVisibleTo(...yourtestassembly...)] YourTests: ...
C.SomethingPrivate_ForTestingPurposesOnly();
... Benefits:
|
Beta Was this translation helpful? Give feedback.
-
In my experience (and, of course, YMMV), a need to directly test the internals of a class is often a code smell indicating that the class is too large and has too many responsibilities. The effect of such tests is usually to make the test suite very brittle, such that implementation of the next feature often requires discarding and rewriting lots of tests that are only peripherally related to the change itself.
If you really want to do this, you can already achieve it with existing features. Here's how I'd do it. Create a file containing the class itself - e.g. public partial class Person
{
// functionality goes here
} Then create a second file, in the same assembly/directory, to contain all the tests - e.g.
|
Beta Was this translation helpful? Give feedback.
-
I've gotten pretty good at creating |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
If you assemble has private/protected/internal methods/properties/inner classes then you should test them somehow. Usually tests methods are located in a separate project special for tests. If you want to test private methods of a project "MyProj.csproj" , you can't include test methods in a project "MyProj.csproj", because test methods will be added in a release build (as I think). But you can't test private methods from a test project "MyProj.Tests.csproj" because they are inaccessible.
Some people advice not to test private/protected/internals methods and classes at all (which is of course a bad advice). Other people advice to use inconvenient hacks, for example InternalsVisibleToAttribute class, PrivateObject class, reflection and etc.
I think there should be a convenient way to write tests for private members. Just to add in C# a keyword "tests", and you can write your tests for private members inside this block:
The main point is when you build a project a compiler avoids to include a code inside a "tests" block in a build. But if you run tests a test runner can find this code inside a "tests" block and test it.
And in order not to pollute a file what a class MyClass is declared in:
and tests in separate files:
You can split a class code and its tests' code into separate files by adding a postfix ".tests" to a class name. And of course a solution explorer should group these related to each other files.
Beta Was this translation helpful? Give feedback.
All reactions