Reputation: 163
With the following code:
class Client {
private Service _service;
public Client() {
_service = new Service; // Connection is made to endpoint
}
public string GetData() {
return _service.ReadData();
}
}
How can Service be mocked using Moq without making modifications to the constructor or access-modifiers?
Upvotes: 0
Views: 250
Reputation: 18556
To emphasize this again:
new Service()
constructor is connecting to the endpoint, you will not be able to mock this code. UnitTests should never depend on another endpoint being available, even if you replace the instance afterwards.Service
does not provide a mockable Interface IService
and its methods are not virtual, you will not be able to mock it at all. You would need to create a wrapper interface and implementationA common workaround would be to create a second constructor with restricted visibility (e.g. internal) and use that to inject a mock into your class. You can control visibility by using the InternalsVisibleTo
attribute. There are some discussions about creating constructors just for tests, but that is a possible first step in the right direction.
class Client {
private Service _service;
// Only for UnitTests
internal Client(Service service) {
_Service = service
}
public Client() {
_service = new Service(); // Connection is made to endpoint
}
public string GetData() {
return _service.ReadData();
}
}
Just putting together the stuff from all the comments into a usable example:
class Client {
private IService _service;
Client(IService service) {
_service = service;
}
public string GetData() {
return _service.ReadData();
}
}
class ClientFactory {
public Client CreateClient(){
var service = new Service(); // Connection is made to endpoint
return new Client(service);
}
}
Upvotes: 1
Reputation: 23174
For that to be possible (or at least, cleanly done), you have to do dependency injection. Your class should not instantiate the Service. It should receive an already instantiated instance of the service.
class Client {
private Service _service;
public Client(Service service) {
_service = service; // maybe you check for null or other checks here
}
public string GetData() {
return _service.ReadData();
}
}
Next step : you should set your class to depend on an interface IService instead of on the actual Service class. This way, you can easily create another ServiceMock that can be injected instead of the Service when you instantiate your class.
class Client {
private IService _service;
public Client(IService service) {
_service = service; // maybe you check for null or other checks here
}
public string GetData() {
return _service.ReadData();
}
}
Related to your requirement :
without making modifications to the constructor or access-modifiers?
That is not a good practice, and it can get really dirty. I don't have a solution like that, given that you use the direct type and not an interface.
I don't know if you are working on some legacy code or third party library, but anticipation on this kind of problems by programming to interfaces instead of classes is the key here.
Upvotes: 3