简体   繁体   中英

Is there another way to unit test business logic in mvc

I have added a unit test to a mvc5 application manually. This is my business logic

public void AddTreatments(TreatmentView model)
{
    using(var treatment = new TreatmentRepository())
    {
        var treat = new PhysiqueData.ModelClasses.Treatment()
        {
            treatmentID = model.treatmentID,
            treatmentCost = model.treatmentCost,
            treatmentDuration = model.treatmentDuration,
            treatmentName = model.treatmentName
        }
        treatment.Insert(treat);
    }
}

This is my repository used in the service layer

public class TreatmentRepository:ITreatmentRepository
{
    private ApplicationDbContext _datacontext;
    private readonly IRepository<Treatment> _treatmentRepository;

    public TreatmentRepository()
    {
        _datacontext = new ApplicationDbContext();
        _treatmentRepository = new RepositoryService<Treatment>(_datacontext);
    }

    public void Insert(Treatment model)
    {
        _treatmentRepository.Insert(model);
    }
}

The next code is my actual unit test for my treatment and it is not working, please can I get some guidance on it. I googled a lot of things and still can't get it right.

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void AddingTreatmenttodatabase()
    {
        //var business = new TreatmentBusiness(new TreatmentRepository());

        var treatment = new Treatment()
        {
            treatmentID = 1,
            treatmentCost = 250,
            treatmentDuration = 45,
            treatmentName = "LowerBack"
        };

        var repositoryMock = new Mock<ITreatmentRepository>();
        repositoryMock.Setup(x => x.Insert(treatment));

        var business = new TreatmentBusiness(repositoryMock.Object);

        business.AddTreatments(treatment);

        repositoryMock.Verify(x => x.Insert(treatment), Times.Once());
    }
}

So you're instantiating a mock of ITreatmentRepository , setting up some behaviour and injecting it into your TreatmentBusiness class. So far, so good.

But then, in your AddTreatments method, you're instantiating a new TreatmentRepository , instead of using the one injected in via the constructor.

I'm assuming your constructor looks something like this:

public class TreatmentBusiness
{
    private readonly ITreatmentRepository repository;

    public TreatmentBusiness(ITreatmentRepository repository)
    {
        this.repository = repository;
    }

    ...
}

In which case, your method should look like this:

public void AddTreatments(TreatmentView model)
{
    using (var treatment= this.repository)
    {
        var treat = new PhysiqueData.ModelClasses.Treatment();
        {
            treat.treatmentID = model.treatmentID;
            treat.treatmentCost = model.treatmentCost;
            treat.treatmentDuration = model.treatmentDuration;
            treat.treatmentName = model.treatmentName;
        }
        treatment.Insert(treat);
    }
}

Notice the usage of the field repository , as opposed to instantiating a new one.

As per Jimmy_keen's suggestion, in order to ensure your repository is properly instantiated and accessible throughout your class, a factory is advisable.

There are several ways you can achieve a repository factory, either you hand crank a dedicated factory and inject that into your constructor, like so:

public class TreatmentBusiness
{
    private readonly ITreatmentRepositoryFactory repositoryFactory;

    public TreatmentBusiness(ITreatmentRepositoryFactory repositoryFactory)
    {
        this.repositoryFactory = repositoryFactory;
    }

    ...
}

And that change the way you access your repository like so:

public void AddTreatments(TreatmentView model)
{
    using (var treatment= this.repositoryFactory.Make())
    //or whatever method name you've chosen on your factory

If you feel this is too heavy handed, you can opt for a method delegate ( Func<> ) and inject just a method that instantiates a new TreatmentRepository .

This would change your constructor like so:

public class TreatmentBusiness
{
    private readonly Func<TreatmentRepository> getTreatmentRepository;

    public TreatmentBusiness(Func<TreatmentRepository> getTreatmentRepository)
    {
            this.getTreatmentRepository = getTreatmentRepository;
    }
    ....
}

And you would change your method like this:

public void AddTreatments(string model)
{
    using (var treatment = this.getTreatmentRepository()) //Or this.getTreatmentRepository.Invoke() - same thing
    {
        ...
    }
}

The way you resolve that dependency is up to you, either do it manually and inject that delegate like this when instantiating your Business object:

var treatmentBusiness = new TreatmentBusiness(() => new TreatmentRepository());

or you can use one of the many IoC containers/DI frameworks out there.

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