简体   繁体   中英

What are the good practice to test/inject private field in C#

My apologies if this a duplicate. I have been given the task to add some coverage for the method and been told to mock the private List<string> property. My question is: Is there a way to test private fields?

The solution I found is adding new constructor just to inject this private list. I am not sure whether this is the right way, so any help will be highly appreciated.

public class Class1
{
    public Class1(List<string> list)//This is just for Unit Testing 
    {
        list1 = list;
    }


    private readonly InjectRepository _repository;
    //
    public Class1(InjectRepository repository)//This is the actual constructor
    {
        _repository = repository;
    }

    private List<string> list1 = new List<string>();

    public void Do_Complex_Logic()
    {
       //list1 will be set with items in it
       //Now list1 is passed to some other instance
    }
}

The private logic of a class should be visible in the public expression of its behavior. In other words, the theory goes, there should be no need to test private fields at all.

There is no way to directly test private fields; they are private after all. If you really think that testing a private field is required, then I would suggest making it internal instead, and exposing it to your unit testing assemblies via the [InternalsVisibleTo] attribute.

That being said, there are frameworks that allow such things, such as TypeMock .

Don't test private fields or methods. Test the behaviour of a contract - public or internal methods.

That said, you can try:

Option A
Make private members internal and set InternalsVisibleTo attribute for the assembly under test.

Option B
Write a wrapper class that wraps you private fields/methods into public ones. This approach has a benefit that you don't need to make your private methods internal. The downside is that for every private method you will have a wrapper public method. So the amount of methods may double.

Adding to what womp & oleksii have already said -

The fact that you want/need to test a private method is a sign (smell?) that your design may be incorrect. This is one of the positive side-effects of TDD (and my personal favorite).

An example:

I don't fully know your domain here, but one simple change could be to, instead of Do_Complex_Logic taking no parameters, you could have it take and return a List<string> :

public List<string> Do_Complex_Logic( List<string> input )

I know - it's simple , but try deconstructing your class a bit and build it back with the tests in mind first.

I'm not disagreeing with the other answers; but in this case I think the most appropriate thing to do - given your code; is to change the signature of

'Do_Complex_Logic'

The real problem is that you are dealing with the 'state' of List1. If you pass List1 into Do_Complex_Logic - your problem is mostly solved. Your comment says that 'Do_Complex_Logic' is going to (presumably) do some work on List1 and then pass it into something else, right?

Make Do_Complex_Logic take a List and return a List. Now it's easy to test. Instead of depending on the state of List1 being setup correctly in your class, you can 'inject the dependency'.

How does the private field get prepared in the first place? If it is through interactions with the repository then you could possibly mock that object.

One way to do that is to interact with your repository through an interface (let's call it IInjectRepository rather than a concrete entity and then use something like Moq (or one of the many plethora of mocking frameworks out there) to populate your data via a mock'd repository as you setup your test. After you have called Do_Complex_Logic I assume you have a way of interrogating the entity so that you know it has done what you expected to. This way you can avoid/mitigate adding methods and classes that are for test only.

You could use reflection as mensioned in answer https://stackoverflow.com/a/3376157/1523402 of a related question.
I don't know if private accessors also work for private fields. But they help (at least) for private methods (see http://msdn.microsoft.com/en-us/library/ms184807%28v=vs.80%29.aspx ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM