Reputation: 6679
While I am aware of this question/answers (Unit testing a method that calls another method), still not sure what is the best approach to unit-test a method that calls another public method on the same class?
I made a sample code (it can be seen here too: dotnetfiddle.net/I07RMg )
public class MyEntity
{
public int ID { get; set;}
public string Title { get; set;}
}
public interface IMyService
{
MyEntity GetEntity(int entityId);
IEnumerable<MyEntity> GetAllEntities();
}
public sealed class MyService : IMyService
{
IRepository _repository;
public MyService(IRepository repository)
{
_repository = repository;
}
public MyEntity GetEntity(int entityId)
{
var entities = GetAllEntities();
var entity = entities.SingleOrDefault(e => e.ID == entityId);
if (entity == null)
{
entity = new MyEntity { ID = -1, Title = "New Entity" };
}
return entity;
}
public IEnumerable<MyEntity> GetAllEntities()
{
var entities = _repository.Get();
//Some rules and logics here, like:
entities = entities.Where(e => !string.IsNullOrEmpty(e.Title));
return entities; // To broke the test: return new List<MyEntity>();
}
}
public interface IRepository : IDisposable
{
IEnumerable<MyEntity> Get();
}
So the question is how to write a unit test that only tests the logic inside MyService.GetEntity(int)
? (while GetEntity
internally calls GetAllEntities()
but I am not interested to test latter one).
Upvotes: 1
Views: 914
Reputation: 745
You can test it by mocking function GetAllEntities(), using mocking framework (I use Typemock Isolator).
Here is a simple example:
[TestMethod, Isolated]
public void TestGetCreatesNewEntity()
{
//Assert
IRepository someRepository = new MyRepository();
MyService service = new MyService(someRepository);
List<MyEntity> entities = new List<MyEntity>();
Isolate.WhenCalled(() => service.GetAllEntities()).WillReturn(entities.AsQueryable());
//Act
MyEntity result = service.GetEntity(1);
//Assert
Assert.AreEqual(-1, result.ID);
Assert.AreEqual("New Entity", result.Title);
}
Hope it'll help you.
Upvotes: 1
Reputation: 774
You can increase your design and testeability creating a virtual GetAllEntities methods, subclass your service with MyTesteableService:
public class MyTesteableService : MyService
{
public override IEnumerable<MyEntity> GetAllEntities()
{
return something;
}
}
Now you can test your new testeable service without use GetAllEntities logic. however, you will have to test the behavior of GetEntity and verify the call to GetAllEntities.
So, in another manner, you can think your service as an abstract class with virtual GetEntity and abstract GetAllEntities. I use RhinoMock and I can do this with PartialMock (http://ayende.com/wiki/Rhino+Mocks+Partial+Mocks.ashx?AspxAutoDetectCookieSupport=1)
Upvotes: 0
Reputation: 64933
I really believe that unit testing isn't about mocking even methods of the same class.
When we talk about units we should refer to parts of your software. That is, you want to test GetEntity
and the fact that it also calls GetAllEntities
under the hoods is just an implementation detail.
What you really need is to be sure that, when you test your service, any injected dependency (repository, other services collaborating in the domain...) should be replaced with fakes to be sure that you're just testing your service, or you'll be implementing an integration test.
I understand. The only caveat is if the logics inside the GetAllEntities() fails, it might also breaks Unit Tests for GetEntity(). Then it's a bit harder to pin point where the real bug lies.
As I said previously on this answer, the fact that GetEntity
calls GetAllEntities
is just an implementation detail. It's like if you would re-implement (argh, copy-paste programming...) the logic of GetAllEntities
inside GetEntity
. Who cares.
Actually, if GetEntity
fails because GetAllEntities
, the test for GetAllEntities
itself will also fail. What test will you try to address first? I suspect that once you've realized that GetEntities
fails because of GetAllEntities
and also GetAllEntities
test fails, you would go directly to fix GetAllEntities
test, won't you?
In summary, the way you've described your concern, the bug would be on GetAllEntities
and it's absolutely expectable that any other method relying on GetAllEntities
could fail too.
Upvotes: 7