Translate

Monday 23 April 2018

A fake - Why faking a Person is a 'fake test'

Just happened to hit on the right word to express it!

While testing with TypeMock, I came across an interesting scenario where a class is declared within a controller that set off this simple post on why a fake is created and what purpose does it solve.

Let me elaborate using this example I found on the web. A controller with a class called Person inside it.

When writing a test for the controller, you will obviously need an instance of a Person because of Parameter Binding but a fake test is written just so that an external dependency does not hinder development!

This is the fundamental point in unit testing. If a unit test handles an external dependency code either through a direct, in place implementation of the external dependency or through accessing it, for real, then it is not unit testing because it violates the principle of testing code in isolation.

So, in this case, typing the Isolator.fake instance as a <Person> is a violation of the principle and therefore, is not even a 'fake test'

Below is the test for the ASP.Net MVC Controller code.

using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MvcApplication1.Controllers;
using TypeMock.ArrangeActAssert;
namespace MvcApplication1.Tests.Controllers
{
    [TestClass]
    public class ValuesControllerTest
    {
        [TestMethod]
        public void Get()
        {
            // Arrange
            ValuesController controller = new ValuesController();

            // Act
            IEnumerable result = controller.Get();

            // Assert
            Assert.IsNotNull(result);
            Assert.AreEqual(2, result.Count());
            Assert.AreEqual("value1", result.ElementAt(0));
            Assert.AreEqual("value2", result.ElementAt(1));
        }

        [TestMethod]
        public void GetById()
        {
            // Arrange
            ValuesController controller = new ValuesController();

            // Act
            string result = controller.Get(5);

            // Assert
            Assert.AreEqual("value", result);
        }

        [TestMethod]
        public void Post()
        {
            // Arrange
            ValuesController controller = new ValuesController();
            var p = Isolate.Fake.Instance<Person>(); // Not a fake because it creates an actual instance of type Person!
            // Act
            p.Name = "Jv";
            p.Age = 12;
            var s=controller.Post("Hello ", p);

            // Assert
            Assert.AreEqual("Hello Jv:12", s);
         
        }

        [TestMethod]
        public void Put()
        {
            // Arrange
            ValuesController controller = new ValuesController();

            // Act
            controller.Put(5, "value");

            // Assert
        }

        [TestMethod]
        public void Delete()
        {
            // Arrange
            ValuesController controller = new ValuesController();

            // Act
            controller.Delete(5);

            // Assert
        }
    }

}

  
using System.Collections.Generic;
using System.Web.Http;

namespace MvcApplication1.Controllers
{
    public class ValuesController : ApiController
    {
        // GET api/values
        public IEnumerable Get()
        {
            return new string[] { "value1", "value2" };
        }

        // PUT api/values/5
        public void Put(int id, [FromBody]string value)
        {
        }

        // DELETE api/values/5
        public void Delete(int id)
        {
        }
        // GET api/values/5
        public string Get(int id)
        {
            return "value";
        }

        // POST api/values
        [HttpPost]
        public string Post(string value, [FromBody]Person p)
        {
            return value+p.Name+":"+p.Age;
        }

    }
    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }

        public override string ToString()
        {
            return this.Name + ": " + this.Age;
        }
    }
}

Basically, the purpose of Agile and Agile testing is to ensure that sufficient agility is always infused into the developmenr process not just through maintaining high confidence levels but also by eliminating sycophantic situations of "you praise me and I will praise you" attitude with testing techniques like using TestDoubles when a dependency is under development or not yet implemented.

And even if implemented, a unit test is not meant to test whether the Person exists or not but whether the code that expects the Person and has to interact with it in some way, either by calling an action on the Person object or by modifying some data of the Person, is behaving as expected of it.

And on the point of dependency, it is also important to note that mock testing using the Dependency Injection way is another example of wiring more dependency into the test itself and hence, a mock test, too, should not be concerned with whether the server is running or whether the database instance is up and running. The mock test should simply be able to mock the object! And this capability should be or rather, can be provided only by the mock framework ie., not as in the moq framework but in a way that the runtime calls are intercepted to fake or mock an object under test.

Happy unit testing, fake-ing and test double-ing !

No comments: