In a called method, Mock does not replace a return value.

859 views
Skip to first unread message

okumu...@gmail.com

unread,
Jan 18, 2018, 2:18:54 AM1/18/18
to NSubstitute
Hello there,

I have tried mocking non-virtual method, and found one solution (extend origin class and override the method as abstract).
Like bellows

class Class1 {
   
public string Say() {
       
return "I am Class1.";
   
}
}

class abstract Class1Wrapper : Class1 {
   
public abstract new string Say();
}

And, create Mock for the extended class, and replace a return value
var class1 = Substitute.For<Class1Wrapper>();
class1
.Say().Returns("I am Substitute.");

In my test class, Mock does replace a return value.
var actual = class1.Say();  // the actual will be 'I am Substitute."


But, passes Mock instance to an other class, Mock does not replace a return value.
class Runner {
   
public string Run(Class1 class1) {
       
return class1.Say(); // This calls the real method of base class, I do not know why.
   
}
}


Is this impossible solution? or something wrong?


Whole source codes are here
  https://github.com/sengokyu/SubstituteConcreteClassExperience

My Environment:
 - NSubstitute 3.1.0
 - .Net Framework 4.5.2

Regards.

okumu...@gmail.com

unread,
Jan 18, 2018, 7:35:47 PM1/18/18
to NSubstitute
I did not describe my problem well.
I post revised.

I want to test Runner class like follows

class Runner {
   
public string Run(Class1 class1)
   
{
       
return class1.Say();
   
}
}


Runner class is depend on Class1 class, Class1 class is like follows

class Class1 {
   
public string Say()
   
{
       
return "I am Class1.";
   
}
}

I do not own Class1 class codes.
I will mock a non-virtual method, and found one solution like follows
abstract class Class1Wrapper {

   
public abstract new string Say();
}
I extend Class1 class and override the non-virtual method.


And I create the mock in usual way
var class1 = Substitute.For<Class1Wrapper>();

class1
.Say().Returns("I am Mock.");  // Replace return value

And test Runner class
var target = new Runner();
var actual = target.Run(class1);

Something strange occurred, actual will be "I am Class1.";
NSubstitute does not replace return value.

Regards.

David Tchepak

unread,
Jan 19, 2018, 8:08:33 PM1/19/18
to nsubs...@googlegroups.com
Hi,
It's a nice idea but, as your second email suggests, it does not work as you are hoping.

The `new` keyword in C# makes a new method of the same name; which one will be called depends on what type reference you call it from.

Let's create a modified wrapper:

public ClassWrapper2 : Class1 {
  public virtual new String Say() { return "wrapper"; }
}

ClassWrapper2 classWrapper = new ClassWrapper2();
Class1 class1Ref = classWrapper; 

classWrapper.Say(); // Calls ClassWrapper2.Say => "wrapper'
class1Ref.Say();     //Calls Class1.Say => "I am Class1."
((Class1) classWrapper).Say();     //Calls Class1.Say => "I am Class1."

(This is all plain C#; no NSubstitute involved)

So when you mock the class wrapper and set a `Returns`, it does do that, but the real code calls it via `Class1 and so the non-virtual method is called and NSubstitute does not get invoked.

One way to deal with code you don't own like this is to create your own adapter for it. In this case:

public class MyWrapper {
  Class1 dep;
  public MyWrapper(Class1 dependency) {
    dep = dependency;
  }
  public virtual string Say() { return dep.Say(); }
}

Now the real code can use the mockable MyWrapper instead of Class1.

Hope this helps.
Regards,
David


--
You received this message because you are subscribed to the Google Groups "NSubstitute" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nsubstitute+unsubscribe@googlegroups.com.
To post to this group, send email to nsubs...@googlegroups.com.
Visit this group at https://groups.google.com/group/nsubstitute.
For more options, visit https://groups.google.com/d/optout.

okumu...@gmail.com

unread,
Jan 22, 2018, 12:38:07 AM1/22/18
to NSubstitute
Thank you David. Your detailed explanation saves me.


Reply all
Reply to author
Forward
0 new messages