Reputation:
How can I call a C# property's public getter from a unit test without calling the class's constructor?
I am currently writing unit tests for a view model.
I am not testing private methods, only public methods.
I am testing any public getters and setters that implement custom logic or validation.
My class contains a private list of items.
My class contains a property which returns some of these items based on an item's property value.
My class contains a constructor which makes a call to a database.
I want to test that this getter returns those items correctly, because the getter is part of the class's public interface and should be tested.
My problem is that I want to somehow mock my class such that I can call the getter of the class property during a unit test. However, I don't want to call the constructor of the class as that would mean calling into the database and performing other work outside of the intended scope of the test.
Here's an example with monkeys and bananas:
public class Monkey {
private List<Banana> bananas;
public Monkey()
{
this.GetBananasFromDatabase()
}
public List<Banana> PeeledBananas {
get {
List<Banana> peeledBananas = new List<Banana>();
foreach(Banana banana in this.bananas)
{
if(banana.IsPeeled)
{
peeledBananas.Add(banana);
}
}
return peeledBananas;
}
}
private GetBananasFromDatabase()
{
this.bananas = Database.GetBananas();
}
}
In this example, I want to test the getter for the PeeledBananas
property.
I don't want to call the constructor, because I don't want to call the GetBananasFromDatabase()
method.
The PeeledBananas
getter is part of the public API of the monkey
class, so it should be unit tested. But the database should not be called during the test.
I could create a new monkey
mock where I can call the constructor anyway and just mock the behavior of the GetBananasFromDatabase()
method, but I can't mock the GetBananasFromDatabase()
method because it's private.
What's a good way to solve this?
Upvotes: 1
Views: 686
Reputation:
Nkosi's comment provided a useful insight. My issue was that my code was tightly coupled due to the use of a concrete database type. By practicing polymorphism and inversion of control (IOC), I decided to pass a database interface to my constructor and call methods on that instead of calling methods on a concrete database.
Now when I want to unit test something, I can just pass in a fake database object into the monkey
constructor and I don't have to touch the GetBananasFromDatabase()
method.
Here's what the code looks like now:
public class Monkey {
private List<Banana> bananas;
private IDatabase database;
public Monkey(IDatabase database)
{
this.database = database;
this.GetBananasFromDatabase()
}
public List<Banana> PeeledBananas {
get {
List<Banana> peeledBananas = new List<Banana>();
foreach(Banana banana in this.bananas)
{
if(banana.IsPeeled)
{
peeledBananas.Add(banana);
}
}
return peeledBananas;
}
}
private GetInfoFromDatabase()
{
this.bananas = this.database.GetBananas();
}
}
Now my monkey
can get bananas
from whatever database
makes sense for the given context, and the monkey
class is no longer tightly coupled to a specific IDatabase
implementation.
When I want to use the monkey
class in production code, I instantiate the class with some RealDatabase
. when I want to unit test the monkey
class, I instantiate the the class with some FakeDatabase
. Both implement IDatabase
and GetBananas()
, so monkey
works no matter what.
Thanks for the tip, Nkosi!
Upvotes: 2