2

I have a class which contains an internal helper such as following code:

[assembly: InternalsVisibleTo("Tests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
namespace NS.B
{
    public class A {
        internal readonly B _bHealper;
        public int GetBag(string s1, string s2){
            return _bHelper.GetBag(s1, s2);
        }
    }
}

[assembly: InternalsVisibleTo("Tests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

namespace NS.B
{
    internal class B 
    {        
         public int GetBag(string str1, string str2){
             /// do some work 
             return result;
         }
    }
}

then I try to mock my helper inside A class and test A class GetBag function by this code:

 [Fact]
    public void checkBaggageRule()
    {
        var repo = Substitute.For<A>();
        repo._bHelper.GetBag(Arg.Any<string>(), Arg.Any<string>()).Returns(30);
        var result = repo.GetBag("oo", "L");
        Assert.True(result != null);
        Assert.True(result == 30);
    } 

but I am getting this exception while I debug my test:

NSubstitute.Exceptions.UnexpectedArgumentMatcherException : Argument matchers (Arg.Is, Arg.Any) should only be used in place of member arguments. Do not use in a Returns() statement or anywhere else outside of a member call.

how can I mock this internal member and pass my test?

Navid_pdp11
  • 3,487
  • 3
  • 37
  • 65
  • See https://stackoverflow.com/questions/40922774/nsubstitute-test-works-by-itself-but-throws-unexpected-matcher-argument-in-a-su – Bernard Vander Beken Jan 09 '19 at 11:04
  • 1
    this does not my case. there is no virtual method and I get this exception when I debug that test case and other tests case are not involved in scenario. – Navid_pdp11 Jan 09 '19 at 11:09
  • @Navid_pdp11 Code samples provided are confusing and conflicts with each other. Clarify with a [mcve] – Nkosi Jan 09 '19 at 11:10
  • I change my B.GetBag as virtual but still, I am getting same error. – Navid_pdp11 Jan 09 '19 at 11:21
  • Why are you mocking the system under test? – Nkosi Jan 09 '19 at 11:32
  • B class is working with DB and fetching data from DB is not my concern now. I just check my business operation in my code. – Navid_pdp11 Jan 09 '19 at 11:33
  • 1
    Then you need to take a look at your current design choices. Difficulty crafting isolated unit tests tends to indicate that there may be problems with the initial design. Like in this case tight coupling that can be considered a code smell. – Nkosi Jan 09 '19 at 11:52
  • 1
    You might want to consider cleaning up the code, in particular this line `internal readonly B _bHealper;`. It doesn't look right to me. – Svek Jan 09 '19 at 12:52

1 Answers1

1

There's not quite enough code in the sample to tell for sure, but I'm not sure that the internal keyword is causing the problem here. If you make them all public instead do you get the same error?

There are a few other possible issues that could be causing problems for you here.

First, try installing the NSubstitute.Analyzers package, which will detect issues like trying to substitute for non-virtual members.

Next, the sample code does not show how A._bHelper gets initialised. Let's update it to use constructor injection, and we'll substitute for the dependency rather than the entire class under test (as pointed out by @Nkosi in the comments).

public class A
{
    public A(MssqlEntityHelper helper) { _bHelper = helper;  }
    internal readonly MssqlEntityHelper _bHelper;
    public int GetBag(string s1, string s2) {
        return _bHelper.GetBag(s1, s2);
    }
}

// Tests:
[Fact]
public void SampleTest() {
    var repo = new NS.B.A(Substitute.For<NS.B.MssqlEntityHelper>());
    repo._bHelper.GetBag(Arg.Any<string>(), Arg.Any<string>()).Returns(30);
    var result = repo.GetBag("oo", "L");
    Assert.True(result == 30);
}

As the NSubstitute.Analyzers package will point out, MssqlEntityHelper.GetBag() will need to be made virtual in order for NSubstitute to work with it:

public class MssqlEntityHelper {
    public virtual int GetBag(string str1, string str2) { ... }
}

Those changes will get a passing test based on the sample code provided. The exact exception you are seeing may be as a result this test or problems in other tests, perhaps attempting to substitute for non-virtual members in earlier tests. Installing the NSubstitute.Analyzers package will hopefully help you find these cases. If this still doesn't resolve the problem there are a few other debugging steps we can try (running the test in isolation, running a single fixture, looking at test logs to see test execution order and seeing if preceding tests are causing problems that bleed into this test, etc.).

David Tchepak
  • 9,826
  • 2
  • 56
  • 68
  • 1
    They would also have more flexibility if the class under test was dependent on an abstraction rather than a concretion. Then there is no worry about virtual/non-virtual members. I would however require a redesign by them of the system. – Nkosi Jan 09 '19 at 21:54
  • @Nkosi Agreed. I think that would also help with the any problems caused by `internal`; could use a public `interface` instead, and have an `internal` helper implementation of that interface injected into production code. – David Tchepak Jan 09 '19 at 22:00