Best approach to breaking dependencies in C #?

We are considering adding unit tests to our C # code base. It's easy for me to add unit tests to simple classes, but classes that interact with other dependencies are harder. I looked at mocking frameworks, but wondered about the best approach to writing classes in the first place, to break external dependencies such as the file system, databases, and messaging systems.

To give an example, the procedure listens on a socket for a message in a specific format - say, MessageA. It is decoded, some calculations are done, it is transcoded into another binary format, and the resulting message is then sent to MessageB.

My current testing approach is as follows. I am extracting an interface for all socket interactions and am creating an interface layout. I installed the interface in singleton mode. Then run the class against hard-coded inputs. The class test will use the singleton interface to send / receive.

I am doing a similar thing for testing database interactions.

This doesn't seem like the most flexible approach, how would you improve this to make it easier to test? If a mocking framework is the answer, how would I design classes?

Code example:

[SetUp]
public void init()
{
    // set message interface in singleton as mock interface
    CommAdapter.Instance.MessageAdapter = new MockMessage();

    // build reference message from hard coded test variables
    initialiseMessageA();

    // set input from mock message socket
    ((MockMessage) CommAdapter.Instance.MessageAdapter).MessageIn = m_messageA;
}

[Test]
public void test_listenMessage_validOutput()
{
    // initialise test class
    MessageSocket tS = new MessageSocket();

    // read from socket
    tS.listenMessage();

    // extract mock interface from singleton
    MockMessage mm = ((MockMessage) CommAdapter.Instance.MessageAdapter);

    // assert sent message is in correct / correstpoinding format
    Assert.AreEqual(1000001, mm.SentMessageB.TestField);

}
+5
source share
2 answers

, Singletons , Injection Dependency DI, Ninject. , .

Ninject , :) (, Unity).

DI, :

class Samurai {
  private IWeapon _weapon;
  public Samurai(IWeapon weapon) {
    _weapon = weapon;
  }
  public void Attack(string target) {
    _weapon.Hit(target);
  }
}

class Shuriken : IWeapon {
  public void Hit(string target) {
    Console.WriteLine("Pierced {0} armor", target);
  }
}

class Program {
  public static void Main() {
    Samurai warrior1 = new Samurai(new Shuriken());
    Samurai warrior2 = new Samurai(new Sword());
    warrior1.Attack("the evildoers");
    warrior2.Attack("the evildoers");
  }
}

, , , . DI, .

:

class Program {
  public static void Main() {
    using(IKernel kernel = new StandardKernel(new WeaponsModule()))
    {
      var samurai = kernel.Get<Samurai>();
      warrior1.Attack("the evildoers");
    }
  }
}

// Todo: Duplicate class definitions from above...

public class WarriorModule : NinjectModule {
  public override void Load() {
    Bind<IWeapon>().To<Sword>();
    Bind<Samurai>().ToSelf().InSingletonScope();
  }
}

-, Moq, - :

[Test]
public void HitShouldBeCalledByAttack()
{
    // Arrange all our data for testing
    const string target = "the evildoers";
    var mock = new Mock<IWeapon>();
    mock.Setup(w => w.Hit(target))
        .AtMostOnce();

    IWeapon mockWeapon = mock.Object;
    var warrior1 = new Samurai(mockWeapon);

    // Act on our code under test
    warrior1.Attack(target);

    // Assert Hit was called
    mock.Verify(w => w.Hit(target));
}

, , . , . .

, DI . , ( , ), .

+7

DI ( MS Unity 2.0 , ) , - MOQ. / :

  • ; , IDbConnection, , .
  • DI
  • DI ( )
+1

All Articles