0

I want to write tests for a program that is coded by someone else. But I have some problems while writing tests. I can't understand exactly how to fake some objects. I searched and found Unit Test for n tier architecture but It doesn't help me. For example, I want to write a test for code below (I know It is a dummy code for just clarification)

        public List<CustomerObject> FetchCustomersByName(CustomerObject obj)
        {
            DAL customerDal = new DAL();
            //Maybe other operations
            List<CustomerObject> list = customerDal.FetchByName(obj.Name);
            //Maybe other operations over list
            return list;
        }

I just want to test FetchCustomersByName but there is connection with DAL. I think creating stub class but In this case I have to change my original code. And it was coded by someone else. How can I write a test for this method?

Thanks in advance.

Community
  • 1
  • 1
PepeDeLew
  • 346
  • 4
  • 15

2 Answers2

1

Don't unit test the data access layer. Write integration tests for it.

Mocking the dependencies in the DAL isn't just worth the trouble as it doesn't guarantee anything.

If you think about it, the DAL have dependencies on the SQL dialect and the database schema. Therefore your unit tests might work just fine. But when you run the real solution it can still fail. The reason can be that your SQL queries are incorrect or that the one of the class property types doesn't match the table column types.

unit tests are typically written for business logic. One thing that they catch is errors that doesn't generate exceptions such as incorrect conditions or calculation errors.

Update

Ok. So your example actually contains business logic. The method name fooled me.

You have to change the way you create your DAL classes. But you don't have to use constructor injection like Jack Hughes suggests. Instead you can use the factory pattern:

    public List<CustomerObject> FetchCustomersByName(CustomerObject obj)
    {
        var customerDal = DalFactory.Create<CustomerDal>();

        //Maybe other operations
        List<CustomerObject> list = customerDal.FetchByName(obj.Name);
        //Maybe other operations over list
        return list;
    }

That's bit easier since now you can just use "replace all" to change all var customerDal = new CustomerDal() to var customerDal = DalFactory.Create<CustomerDal>();

In that factory class you can call different implementations

public class DalFactory
{
    public static IDalFactory Factory { get set; }

    static DalFactory()
    {
        Factory = new DefaultDalFactory();
    }


    public static T Create<T>() where T : class
    {
        return Factory.Create<T>();
    }
}

public interface IDalFactory
{
    T Create<T>() where T : class
}

public class DefaultDalFactory : IDalFactory
{
    public T Create<T>() where T : class
    {
        return new T();
    }
}

The code isn't beautiful, but it solves your case with minimal refactoring. I suggest that you start with that and then try to change your coding standards so that constructor injection is allowed.

To get it working in your tests you can use the following implementation. It uses [ThreadStatic] to allow multiple tests to run at the same time.

public class TestDalFactory : IDalFactory
{
    [ThreadStatic]
    private static Dictionary<Type, object> _instances;

    public static Dictionary<Type, object> DalInstances
    {
        get
        {
            if (_instances == null)
                _instances = new Dictionary<Type, Object>();
            return _instances;
        }
   }

    public static TestDalFactory Instance = new TestDalFactory();

    public T Create<T>() where T : class
    {
        return (T)_instances[typeof(T)];
    }
}

Next in your tests you can configure the DAL factory to return a mock by doing the following:

[TestClass]
public class MyBusinessTests
{
    [TestInitialize]
    public void Init()
    {
        DalFactory.Instance = TestDalFactory.Instance;
    }

    [TestMethod]
    public void do_some_testing_in_the_business()
    {
        TestDalFactory.Instance.DalInstances[typeof(CustomerDal)] = new MyNewMock();

        //do the testing here
    }
}
jgauffin
  • 99,844
  • 45
  • 235
  • 372
1

Using constructor injection of the DAL would allow you to stub the DAL layer. Ideally you would inject an interface. Mocking concrete classes is a bit of a pain with the tools I've used. Commercial mocking tools may well be better suited to mocking concrete classes but I've not used any of those.

class YourClass
{
    private DAL customerDal;

    public YourClass(DAL theDal)
    {
        customerDal = theDal;
    }

    public List<CustomerObject> FetchCustomersByName(CustomerObject obj)
    {
       // don't create the DAL here...
       //Maybe other operations
       List<CustomerObject> list = customerDal.FetchByName(obj.Name);
       //Maybe other operations over list
       return list;
     }
}

[Test]
public void TestMethodHere()
{
    // Arrange
    var dalMock = Mock.Of<DAL>();
    // setup your mock DAL layer here... need to provide data for the FetchByName method
    var sut = new YourClass(dalMock);

    // Act
    var actualResult = sut.FetchCustomersByName(new CustomerObject());

    // Assert
    // Your assert here...
}
Jack Hughes
  • 5,514
  • 4
  • 27
  • 32
  • In fact, It seems the only way for my code. But I can't change the way how I create DAL because of the coding standarts of our software. So I started to think that I can't write a unit test for our code. – PepeDeLew Sep 30 '14 at 09:40
  • If you can't change the code then unit testing would be very difficult. You could still write integration tests with the database system seeded with known data. May be better than nothing but, in the long term, you'll find test runs getting excessively long. It may be that your coding standards are specifically designed to make writing unit tests impossible because, whoever wrote the standard, doesn't believe in unit testing. In which case, even attempting any kind of automated testing, may place you in difficult political territory. – Jack Hughes Sep 30 '14 at 09:51