Roomey
Roomey

Reputation: 716

How to setup returned object from static method? UnitTesting

Lets say I have a method A

public class ClassWithAMethod
{
   public static void A(string email)
   {
      var b = SomeClass.StaticMethod();
      var c = b.GetUser(x => x.Email == email);
   
      if (c != null)
      {
          throw new Exception();
      }
   }
}


And I need to test a method Abc, which calls method A. For example

public void Abc()
{
   ClassWithAMethod.A("[email protected]");
}

So I need to mock object b and setup method GetUser but how can I do that?

Upvotes: 0

Views: 122

Answers (2)

Peter Csala
Peter Csala

Reputation: 22759

As the adage goes: You can solve every problem with another level of indirection, except for the problem of too many levels of indirection Reference

By introducing a wrapper / adapter your solution become a bit more loosely coupled.

Contract

So, first let's introduce the following interface:

public interface IBFactory
{
  B Create();
}

Wrapper

Let's create the wrapper:

public class BWrapper: IBFactory
{
  public B Create()
  {
    return SomeClass.StaticMethod();
  }
}

Alternative

In case of C# 8 you can combine the previous two steps into one by relying on the default implementation in interface feature:

public interface IBFactory
{
  public B Create()
  {
    return SomeClass.StaticMethod();
  }
}

Relying on abstraction

Now let's modify the ClassWithAMethod class to rely on the IBFactory:

public class ClassWithAMethod
{
   private readonly IBFactory _bFactory;
   public ClassWithAMethod(IBFactory bFactory)
   {
     this._bFactory = bFactory;
   }
   
   public static void A(string email)
   {
      var b = this._bFactory.Create();
      var c = b.GetUser(x => x.Email == email);
   
      if (c != null)
      {
          throw new Exception();
      }
   }
}

You can register the BWrapper as the default implementation for IBFactory in your DI container. Or if you are not using DI nor C# 8 then you can specify a parameterless ctor where you rely on BWrapper. Please bear in mind that this approach is not advised. It is more like a last resort.

public class ClassWithAMethod
{
   private readonly IBFactory _bFactory;

   public ClassWithAMethod()
   {
     this._bFactory = new BWrapper();
   }

   public ClassWithAMethod(IBFactory bFactory)
   {
     this._bFactory = bFactory;
   }

   ...
}

Unit testing

Now you can mock that dependency as well.

var bFactoryMock = new Mock<IBFactory>();
bFactoryMock.Setup(factory => factory.Create()).Returns(...);

var SUT = new ClassWithAMethod(bFactoryMock.Object);

Upvotes: 1

Isitar
Isitar

Reputation: 1449

Without modification, i don't think this is possible.

a slight modification and non breaking change could be to add an optional param bGetter to get the b. You can then create your own bGetter in your unit test.

public class ClassWithAMethod
{
   public static void A(string email, Func<B> bGetter = null)
   {
      var b = null == bGetter ? SomeClass.StaticMethod() : bGetter();
      var c = b.GetUser(x => x.Email == email);
   
      if (c != null)
      {
          throw new Exception();
      }
   }
}

Upvotes: 3

Related Questions