简体   繁体   中英

how to test this business logic

Hi I would really be happy if someone can help me with unit testing this business logic on Visual Studio unit test..

I've google"ed" and checked different ways of unit testing but I keep finding the unit tests for controllers which I don't need.. I'd be most happy somebody can help me know how to unit test this method.

This is my Business class

public void AddConsultation(ConsultationView cv, int patientid)
{

    using (var deprepo = new ConsultationRepository())
    {
        if (cv.ConsultId == 0)
        {
            var curr = DateTime.Now;
            string date = curr.ToString("d");
            string  time= curr.ToString("t");
            var patient = da.Patients.ToList().Find(x => x.PatientId == patientid);

            Consultation _consultation = new Consultation
            {
              ConsultId = cv.ConsultId,
              ConsultDate = date,
              ConsultTime = time,
              illness = cv.illness,
              PresribedMed = cv.PresribedMed,
              Symptoms = cv.Symptoms,
              U_Id = patient.PatientId,
            };

            deprepo.Insert(_consultation);
        }
    }
}

This is my Repository Class

public class ConsultationRepository:IConsultationRepository
{
    private DataContext _datacontext = null;
    private readonly IRepository<Consultation> _clinicRepository;

    public ConsultationRepository()
    {
        _datacontext = new DataContext();
        _clinicRepository = new RepositoryService<Consultation>(_datacontext);

    }

    public Consultation GetById(int id)
    {
        return _clinicRepository.GetById(id);
    }

    public List<Consultation> GetAll()
    {
        return _clinicRepository.GetAll().ToList();
    }

    public void Insert(Consultation model)
    {
        _clinicRepository.Insert(model);
    }

    public void Update(Consultation model)
    {
        _clinicRepository.Update(model);
    }

    public void Delete(Consultation model)
    {
        _clinicRepository.Delete(model);
    }

    public IEnumerable<Consultation> Find(Func<Consultation, bool> predicate)
    {
        return _clinicRepository.Find(predicate).ToList();
    }

    public void Dispose()
    {
        _datacontext.Dispose();
        _datacontext = null;
    }
}

You can make factories to create a different repository for test

//Interface for a factory class
public interface IFactory
{
    IIConsultationRepository Create();
}

Create two factories, one for test and one for production

public class MyFactory : IFactory
{
    public IIConsultationRepository Create()
    {
        return new ConsultationRepository();
    }
}

public class MyTestFactory : IFactory
{
    public IIConsultationRepository Create()
    {
        return new ConsultationTestRpository();
    }
}

Create two repositories. One for test and one for production

public class ConsultationTestRpository : IConsultationRepository
{
    //Your test repository. In this you skip the database.
    //This is just one simple example of doing it.
    Consultation _consultation;
    public Consultation GetById(int id)
    {
        return _consultation;
    }


    public void Insert(Consultation model)
    {
        _consultation = model;
    }

}

public class ConsultationRepository : IConsultationRepository
{
    //Your repository
}

Use this for production

var obj = new TheConsultationClass(new MyFactory());

And this for test

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        var objForTesting = new TheConsultationClass(new MyTestFactory());

        var consultationView = new ConsultationView();



        objForTesting.AddConsultation(consultationView, 123);

        var consultation = objForTesting.GetById(...);

        Assert.AreEqual(123, consultation.U_Id );
    }
}

EDIT

I forgot to show how to use the Factory. Send it as an parameter to the constructor, and then call Factory.Create()

public class TheConsultationClass
{
    public MyFactory Factory { get; set; }

    public TheConsultationClass(IFactory factory)
    {
        Factory = factory;
    }

    public void AddConsultation(ConsultationView cv, int patientid)
    {

        using (var deprepo = Factory.Create())
        {
            if (cv.ConsultId == 0)
            {
                var curr = DateTime.Now;
                string date = curr.ToString("d");
                string time = curr.ToString("t");
                var patient = da.Patients.ToList().Find(x => x.PatientId == patientid);

                Consultation _consultation = new Consultation
                {
                    ConsultId = cv.ConsultId,
                    ConsultDate = date,
                    ConsultTime = time,
                    illness = cv.illness,
                    PresribedMed = cv.PresribedMed,
                    Symptoms = cv.Symptoms,
                    U_Id = patient.PatientId,
                };

                deprepo.Insert(_consultation);
            }
        }
    }
}

If you're read examples of testing controllers and understood them, testing normal classes and methods should be quite straightforward. It is basically the same, but you have more control over your class structure.

As it stands, your code has some issues that make it hard to test. Lets look at your existing code:

public void AddConsultation(ConsultationView cv, int patientid)
{

The line below creates a new ConsultantRepository. This is hard to mock, which means that it's hard to check that the repository is being called correctly. A better approach is to either inject the repository, or a factory into class via its constructor. This can be hand rolled, as described by @AxdorphCoder, or you can make use of an IOC container like CastleWindsor or Ninject to do some of the heavy lifting for you.

    using (var deprepo = new ConsultationRepository())
    {
        if (cv.ConsultId == 0)
        {

The next line references DateTime.Now . Again, this can be quite hard to test. Again, the easiest solution if you need to know that a particular date is used is to either inject a factory for the date, or the inject that date. An alternate if it doesn't need to be that accurate is to store the time at the beginning of your test and then validate that the time used is between the time your test started and the time your test assertions are made.

            var curr = DateTime.Now;
            string date = curr.ToString("d");
            string  time= curr.ToString("t");

The next line references da . This isn't initialized anywhere in the code sample you've given. It looks like it's another repository... in which case the advice above about injecting the repository stands. As an aside, what happens if patient isn't found?

            var patient = da.Patients.ToList().Find(x => x.PatientId == patientid);

            Consultation _consultation = new Consultation
            {
              ConsultId = cv.ConsultId,
              ConsultDate = date,
              ConsultTime = time,
              illness = cv.illness,
              PresribedMed = cv.PresribedMed,
              Symptoms = cv.Symptoms,
              U_Id = patient.PatientId,
            };

            deprepo.Insert(_consultation);
        }
    }
}

So, how would you go about testing the AddConsultant method? Lets assume you're going to follow the AAA pattern for test arrangement . Here are some tests you might write:

  1. Validate Repository Is Not Updated If ConsultId Is Already Set

     Arrange - Setup mock repository - setup da.Patients to contain Patient with correct id - Create system under test injecting repository - Create view, with ConsultId <> 0 Act - Call sut.AddConsultation(view, somePationId) Assert - No methods called on injected repository 
  2. Validate Consultation Created With Values From View

     Arrange - Create View to pass, containing consultId = 0 - setup da.Patients to contain Patient with correct id Store datetime test started Create mock Repository and set it up to expect a call to insert Create system under test and Inject respository Act - Call sut.AddConsultation(view, somePationId) Assert - Assert Insert on respository was called with a consultation Assert each property of consulation has expected values from Arrange Assert consultation date is >= Arrange Date and <= Now 
  3. Validate that AddConsultation Fails if Patient Not Found

     Arrange - Create View to pass, containing consultId = 0 - setup da.Patients, that doesn't contain Patient with correct id Create mock Repository and set it up to not expect call Create system under test and Inject respository Act - Call sut.AddConsultation(view, somePationId) Assert - Assert exception thrown with correct information (currently not done) 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM