Search code examples
c#unit-testingwebformsautofac

how can I inject DateTimeProvider / wrapper dependency in a component's class but not using the constructor?


in a webforms application, let's say we have a class that has a method that does some job with the use of DateTime.Now, if I want to test this method I can inject a DateTimeWrapper using the constructor but in my scenario I don't want the IoC container to construct the class for me, instead I want to use it "new it" and have it testable and I think it makes no sense that each time we want to use this class, a dependency of the DateTimeWrapper "which returns the real DateTime.Now" should be injected!

Note: nothing is being injected in the WebForms page class.

public class EmailNotification
        {
            public IDateTimeWrapper DateTimeWrapper { get; set; }
            public void SetBody(string body)
            {
                ...
                Body = body + DateTimeWrapper.Now.ToString();
            }
        }

I tried to use Property injection using "Autofac" and the following works perfect:

builder.RegisterType<DateTimeWrapper>()
                .As<IDateTimeWrapper>();

builder.RegisterType<EmailNotification>().PropertiesAutowired();

// in this case, it works
var email = container.Resolve<EmailNotification>();
email.SetBody("hello world!");

But I need to use the component like this:

// somehow I want the DateTimeProvider "the real one" to be injected or used at this point
var email= new EmailNotification();
email.SetBody("hello world!");
// I'm getting a NullReferenceException at the last line because the dependency has been never injected

do you have an idea? thanks in advance


Solution

  • the solution was simple "i think", I changed this:

    public class EmailNotification
            {
                public IDateTimeWrapper DateTimeWrapper { get; set; }
                public void SetBody(string body)
                {
                    ...
                    Body = body + DateTimeWrapper.Now.ToString();
                }
            }
    

    to the following:

    public class EmailNotification
    {
        private IDateTimeWrapper _dateTimeWrapper = new DateTimeWrapper();
    
        public IDateTimeWrapper DateTimeWrapper 
        {
            get
            {
                return _dateTimeWrapper;
            }
            set
            {
                _dateTimeWrapper = value ?? throw new ArgumentNullException(nameof(DateTimeWrapper));
            }
        }
        public void SetBody(string body)
        {
                ...
                Body = body + DateTimeWrapper.Now.ToString();
        }
    }
    

    so I still have IDateTimeWrapper as a seam for unit testing and at the same time it gets initialized by default when constructing an instance of the EmailNotification class.