문제

I have a service and i want test without using the "real" repository, but a mock repository. the main program

ServiceManager service = new ServiceManager();
Customer c = new Customer();
//set customer properties
service.Save(c);

the service

public class ServiceManager
{
    public bool Save(Customer customer)
    {
        Repository rep = new Repository();
        bool res = rep.Save((customer));

        return res;
    }
}

in test, i don't want save really the customer. using mock, i can create the repository

var rep = new Mock<Repository>();
rep.Setup(x => x.Save(customer)).Returns(true);

but how can i call in test the service using this mock repository and not the real repository?

if possible i don't want create service like new ServiceManager(repository);

another way is use a factory of repository that return the mock if we are in test and the real repository when in real world, such as

public static class repositoryFactory
{
    private static Repository mockRepository;
    public static void setRepository(Repository rep){
        mockRepository = rep;
    }

    public static Repository getRepository(){
        if (mockRepository == null)
            return new Repository();
        else
            return mockRepository;
    }
}

in this case in test i use setRepository to save the mock. which is the best solutions adopted in general?

도움이 되었습니까?

해결책

The classic way is to use Inversion of Control (that means that ServiceManager is no longer in control of creating Repository), commonly achieved by Dependency Injection (that means the dependency Repository is "injected" into SericeManager).


You seem to already know that, since you wrote

if possible i don't want create service like new ServiceManager(repository);

You can, to achieve this, use an optional argument in the constructor of ServiceManager, like:

public class ServiceManager
{
    private readonly IRepository rep;

    public ServiceManager(IRepository rep=null)
    {
         _rep = rep ?? new Repository();      
    }

    public bool Save(Customer customer)
    {
        bool res = _rep.Save((customer));
        return res;
    }
}

to be able to simply use new ServiceManager() in your application and use new ServiceManager(repositoryMock) in your tests.


However, if you use a IOC-Container (like e.g. StructureMap), injecting dependencies is plain easy: you can leave the creation of objects to the IOC-Container and keep your ServiceManager and other classes "clean" of creating instances of dependend classes manually.

다른 팁

if possible i don't want create service like new ServiceManager(repository);

If this is an absolute requirement, you'll need to use heavy handed tools like Fakes / Moles to mangle a moled repository into your ServiceManager SUT.

However, since this is tagged moq, if you do decouple your dependencies (e.g. via Setter constructor injection), then you can unit test and Mock in isolation. Its a small overhead to make for testable code.

public class ServiceManager
{
    private readonly Repository _repository;

    public ServiceManager(Repository repository)
    {
        _repository = repository;
    }

    public bool Save(Customer customer)
    {
        bool res = _repository.Save((customer));

        return res;
    }
}

And your unit test will now be able to create the ServiceManager (the SUT) and inject the Mocked repository.

And even better would be to abstract repository into an interface, IRepository, and then reduce the dependency of ServiceManager to Repository to an interface. Your moq repo is then var rep = new Mock<IRepository>(); and the dependency becomes private readonly IRepository _repository;.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top