0

Starting with unit testing, I wonder how to test our old code, that looks something like that:

public Player DetermineWinner(Player a, Player b)
{
   if(a.Age>b.Age)
    ....  
   ///few more conditions checking properties of both players
}

class Player
{
  public Player (DBConnection c, world w, DateTime logTime)
  {} //not easy to mock...
}

How to mock that? I understand that if the Player implemented an interface, I could simply create a mock and pass it in the unit test with desired values, but that is not the case here. The Player class is instantiated using various parameters so I cannot simply create an instance during the unit test and pass it - it depends on various external objects. I would need to mock the Player object and set its properties at the same time so that the tests are deterministic. What is the best approach to start with?

Should I next time use interfaces to decouple that?

John V
  • 4,855
  • 15
  • 39
  • 63
  • 1
    Put all dependencies of class `Player` which uses some externals resources (`DbConnection` at least) behind interface or make their methods virtual. Then you can test `Player` class with "mocked" dependencies. Other approach use some "not stricted" mocking framework which will do it for you – Fabio Feb 13 '17 at 19:39
  • The *real* problem here is not "how to mock that", but the very poor design of the code being tested. Specifically, the `Player` class should have no dependency on `DBConnection`. Instead, its constructor should only take data items that truly belong to a player, with database access for reading/writing player data done somewhere else. – Rogério Feb 15 '17 at 14:52
  • What is your Visual Studio edition? – zaitsman Feb 27 '17 at 12:01

2 Answers2

1

you can unit-test your code without changing it, by using a framework like Typemock that let you mock concrete dependency with no need for added interfaces and when you choose a class to be mock (in this case "Player", typemock will also automatically mock all the dependencies in it).

On your given example:

public class UnitTest1
    {
        [TestMethod]
        public void TestDetermineWinner_B_IsTheWinner()
        {
            var P1 = Isolate.Fake.Instance<Player>();
            var P2 = Isolate.Fake.Instance<Player>();

            Isolate.WhenCalled(() => P1.Age).WillReturn(0);
            Isolate.WhenCalled(() => P2.Age).WillReturn(1);

            var result = new ClassUnderTest().DetermineWinner(P1, P2);

            Assert.AreEqual(P2, result);
        }
    }
    public class ClassUnderTest
    {
        public Player DetermineWinner(Player a, Player b)
        {
            if (a.Age > b.Age) { return a; }
            return b;
        ///few more conditions checking properties of both players
        }
    }
    public class Player
    {
        public Player(DbConnection c, world w, DateTime logTime)
        { } //not easy to mock...

        public int Age { get; internal set; }
    }
JamesR
  • 745
  • 4
  • 15
  • You sort of forgot to mention that you have to pay for Typemock :) – zaitsman Feb 27 '17 at 12:01
  • But why its a problem that you need to pay for something that make your life easier? – JamesR Mar 01 '17 at 13:32
  • Because it is much more prudent to buy VS enterprise -> you get this same functionality PLUS the raft of other things (like historical debugging, ADO.net traces etc) – zaitsman Mar 01 '17 at 20:48
0

I think you do not understand what mocking really means.

A mocked object has nothing to do with the "real" implementation of that class. It just "looks" like an object of that class.

But you (resp. the mocking framework) are in control regarding the behavior of that object. Well, unless the field is private (see here on that). So: when your Player fields are not private, you don't have a problem.

Without a mocking framework - you really can't do much. If at all, you could be mocking all the objects that are required as parameter to the constructor of your class under test.

In other words: in the end, you need some object "x" of class "X"; and when using the "real" class "X" gives you so much trouble, then you have to replace it with something that looks like "X".

Worst case, you could have two different versions of the X class; but that would make things pretty complicated.

Community
  • 1
  • 1
GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • Yes, it just looks like one, but how can I create it during unit testing (assume no framework is available)? I would be grateful for example using the my snippet. – John V Feb 13 '17 at 19:50
  • Put in some updates for you, but I fear there isn't too much that will help you. When you cant modify the source code, and you cant use a mocking framework, then your are kinda broken. – GhostCat Feb 14 '17 at 07:34