I was under the impression that mocking is faking data calls thus nothing is real. So when I trying to create my own Unit Test that are very similar to what other developers on my team are doing, I am thinking that this is not correct to be newing up the service.
[Test]
public void TemplateService_UpdateTemplate_ContentNameNotUnique_ServiceReturnsError()
{
Template Template = new Template()
{
TemplateId = 123,
};
// Arrange.
TemplateUpdateRequest request = new TemplateUpdateRequest()
{
ExistingTemplate = Template
};
var templateRepo = new Mock<ITemplateRepository>();
var uproduceRepo = new Mock<IUProduceRepository>();
templateRepo.Setup(p => p.IsUniqueTemplateItemName(215, 456, "Content", It.IsAny<string>())).Returns(false);
templateRepo.Setup(p => p.UpdateTemplate(request)).Returns(1);
// Act.
TemplateService svc = new TemplateService(templateRepo.Object, uproduceRepo.Object);
TemplateResponse response = svc.UpdateTemplate(request);
// Assert.
Assert.IsNotNull(response);
Assert.IsNotNull(response.data);
Assert.IsNull(response.Error);
}
So my issue is with this code:
TemplateService svc = new TemplateService(templateRepo.Object, uproduceRepo.Object);
Should the TemplateService really be newed up? What "if" the Service ended up hitting a database and/or file system? Then it becomes an integration test, and no longer a unit test, right?
TemplateResponse response = svc.UpdateTemplate(request);
Also, how do I really control whether this is truly going to pass or not? It is relying on a service call that I didn't write, so what if there is a bug, or encounters a problem, or return NULL, which is exactly what I do not want! ?
If you're testing TemplateService
, then YES it should be newed up. How else can you test your actual implementation? The point here is to only test TemplateService
and not it's dependencies (unless it's an integrations test).
So if you got a repo like IUProduceRepository
it should be mocked, simply to ensure that you're not writing to some database, and to make sure that you can create a specific scenario.
Your goal is to test that TemplateService
is doing the right thing, based on a specific scenario. So let's say that your IUProduceRepository
is throwing an error that your TemplateService
should handle. Then you should mock that error in your IUProduceRepository
and test your implementation of TemplateService
and make sure that it handles it as expected.
So to answer your questions...
Should the TemplateService really be newed up? What "if" the Service ended up hitting a database and/or file system? Then it becomes an integration test, and no longer a unit test, right?
Yes, I would say that it would be an integrations test. You mock things to make sure that the service isn't writing to DB.
Also, how do I really control whether this is truly going to pass or not? It is relying on a service call that I didn't write, so what if there is a bug, or encounters a problem, or return NULL, which is exactly what I do not want! ?
Your goal is to make sure that the class you test is doing what you expect in a specific scenario. Let's say that your repo is throwing an exception for some reason, then you might want to test that your service can handle that (or at least test that it's doing what's expected).
public class TemplateService : ITemplateService
{
ITemplateRepository _templateRepo;
IUProduceRepository _produceRepo
public TemplateService(ITemplateRepository templateRepo, IUProduceRepository produceRepo)
{
_templateRepo = templateRepo;
_produceRepo = produceRepo;
}
public TemplateResponse UpdateTemplate(Template template)
{
try
{
var result = _templateRepo.Update(template);
return result;
}
catch(Exception ex)
{
// If something went wrong. Always return null. (or whatever)
return null;
}
}
}
[Test]
public void TemplateService_UpdateTemplate_ShouldReturnNullOnError()
{
Template template = new Template() // note that I changed the variable name.
{
TemplateId = 123,
};
// Arrange.
TemplateUpdateRequest request = new TemplateUpdateRequest()
{
ExistingTemplate = template
};
var templateRepo = new Mock<ITemplateRepository>();
var uproduceRepo = new Mock<IUProduceRepository>();
// Mock exception
templateRepo.Setup(p => p.UpdateTemplate(request)).Throw(new ArgumentException("Something went wrong");
// Act.
TemplateService svc = new TemplateService(templateRepo.Object, uproduceRepo.Object);
TemplateResponse response = svc.UpdateTemplate(request);
// Assert.
// Make sure that the exception is handled and null is returned instead.
Assert.IsNull(response);
}
The ONLY thing you actually tested in the case above is that your service will return null
if there's an error in your repo. That way, you have designed a test for a specific scenario, and made sure that your service is doing what's expected when that scenario occurs.
I know that you specifically mentioned that you didn't want it to return null
, but it's more of the concept I'm talking about. So let's say that the repo returns null
... Write a test for it. Mock the repo to return null
, and then test that your service is doing what it should (logging, etc).
so what if there is a bug, or encounters a problem, or return NULL, which is exactly what I do not want! ?
That's what unit testing is all about. Find a scenario, and then make sure that the class you're testing is doing it's part. Mock your dependencies to create that specific scenario.
When I write my tests I have a lot of tests that simply asserts that a method was called. Let's say that I have a service, which only responsibility is to call the repo. Then you might want to write three tests