Search code examples
c#.netnunitmoq

Implement a list of interfaces during Unit Test using NUnit


I'm currently studying C# and I'm quiet stunned over a simple task. I have this code to test:

    public interface IAppointment
{
    public string PatientName { get; set; }
    public IEnumerable<DateTime> ProposedTimes { get; set; }
    public DateTime? SelectedAppointmentTime { set; }
}

public static class MedicalScheduler
{
    public static Dictionary<DateTime, string> Appointments { get; set; } = new Dictionary<DateTime, string>();
    public static List<DateTime> FreeSlots { get; set; } = new List<DateTime>();

    public static IEnumerable<Tuple<string, bool>> Schedule(IEnumerable<IAppointment> requests)
    {
        bool slotFound = false;
        foreach (var appointment in requests)
        {
            if (slotFound) continue;

            foreach (var times in appointment.ProposedTimes)
            {
                var freeSlot = FreeSlots.Where(s => s.Date == times.Date).FirstOrDefault();

                if (freeSlot != null)
                {
                    slotFound = true;
                    Appointments.Remove(freeSlot);
                    appointment.SelectedAppointmentTime = freeSlot;
                    yield return new Tuple<string, bool>(appointment.PatientName, true);
                }
            }

            yield return new Tuple<string, bool>(appointment.PatientName, false);
        }
    }
}

And I'm required to test "Schedule" with a certain set of parameters. For example, I need to test it with empty Appointments and FreeList but with a single element in "requests". I think I have understood how to compile a Unit Test and to set the Dictionary and List parameters. But I'm not sure how to create the IEnumerable variable. My idea was to create a List of IAppointment(s), but how can I implement the interface in the test unit? I have tried using Moq but I didn't understood how I should use it correctly.

I'm sorry if the request seems quite confusing, but I don't know how to explain better :)

Thanks in advance for the help.


Solution

  • Please see the following example:

    [Test]
    public void Schedule()
    {
        // Arrange
        var appointmentMock = new Mock<IAppointment>();
        appointmentMock.Setup(appointment => appointment.PatientName).Returns("Dixie Dörner");
        appointmentMock.Setup(appointment => appointment.ProposedTimes).Returns(
            new List<DateTime>
            {
                new DateTime(1953,4,12), 
                new DateTime(1953,4,13)
            });
        var requests = new List<IAppointment>{appointmentMock.Object};
    
        // Act
        var results = MedicalScheduler.Schedule(requests);
    
        // Assert
        Assert.IsTrue(results.Any());
        // results.Should().HaveCount(1); // If you're using FluentAssertions
    }
    

    MedicalScheduler.Schedule accepts any parameter implementing IEnumerable<IAppointment>, e. g. List<IAppointment> or Collection<IAppointment>.

    So you simply create a List<IAppointment> and fill it with custom instances of IAppointment.

    You can use Moq for creating the instances, as I did in the example. But for my own projects, I prefer the builder pattern:

    internal static class AppointmentBuilder
    {
        public static IAppointment CreateDefault() => new Appointment();
    
        public static IAppointment WithPatientName(this IAppointment appointment, string patientName)
        {
            appointment.PatientName = patientName;
            return appointment;
        }
        
        public static IAppointment WithProposedTimes(this IAppointment appointment, params DateTime[] proposedTimes)
        {
            appointment.ProposedTimes = proposedTimes;
            return appointment;
        }
        
        private class Appointment : IAppointment
        {
            public string PatientName { get; set; }
            public IEnumerable<DateTime> ProposedTimes { get; set; }
            public DateTime? SelectedAppointmentTime { get; set; }
        }
    }
    
    [Test]
    public void Schedule()
    {
        // Arrange
        var requests = new List<IAppointment>{AppointmentBuilder.CreateDefault()
            .WithPatientName("Dixie")
            .WithProposedTimes(new DateTime(1953,4,12))};
    
        // Act
        var results = MedicalScheduler.Schedule(requests);
    
        // Assert
        Assert.IsTrue(results.Any());
        // results.Should().HaveCount(1); // If you're using FluentAssertions
    }