views:

477

answers:

5

Question

How do you handle read-only fields when creating fakes?

Background

I'm in the beginner stages of using ASP.Net MVC and am using Steven Sanderson's Sports Store and Scott Gu's Nerd Dinner as examples. One small problem that I've just hit is how to work with read-only properties when doing fakes. I'm using LINQToSQL.

My interface is:

public interface IPersonRespository
{   
    Person GetPerson(int id);
}

and my fake becomes

public class FakePersonRepository
{
    public Person GetPerson(int id)
    {
        return new Person {id="EMP12345", name="John Doe", age=47, ssn=123-45-6789, totalDrWhoEpisodesWatched=42};
    }
}

Here's my problem. The fields id, ssn and totalDrWhoEpisodesWatched are read-only, so the above code won't actually work. However, I don't recognize how to create a fake new person and set a read-only property. I'm sure there is a solution, but I haven't come across it yet in my searches.

Update: Inheritance + Property Hiding as a Potential Solution?

I haven't yet decided upon a firm solution to the problem. I dislike the notion of modifying my Domain classes for the purposes of creating fakes. To me, adding markup to the domain classes in order to do testing is a form of added coupling -- coupling to the implementation of your test. I'm now investigating another possibility, which is to create a FakePerson class, which inherits from Person, but hides the properties with new read-write properties.

public class FakePerson: Person
{
    public new int age { get; set; }
    public new string ssn { get; set; }
    public new int totalDrWhoEpisodesWatched { get; set; }
}

So far, this solution is how I am leaning. It does break the Liskov Substitution Principle, however that doesn't bug me as much in a test project. I'd be glad to hear any criticism and/or feedback on this as a solution.

Winner: Mock Frameworks

Moq appears to do the job. My last solution of hiding the property through inheritance does, in fact, work, however by using Moq, I get a standardized set of functionality that is more maintainable. I assume that other mock frameworks have this functionality, but I haven't checked. Moq is said to be more straightforward for the beginning mock writing, which I definitely am right now.

+1  A: 

You can only set readonly properties in the constructor of the class. The Person object should have a constructor that accepts id, ssn, and totalDrWhoEpisodesWatched. Of course, if this is a linqtosql generated object, you might have issues modifying that as the code is auto-generated.

You could consider using a mapped object to expose in your repository ... so you'd never actually have to use your linqtosql object as your model.

Joel Martinez
@Joel- Do you find the overhead of using a completely POCO model worth the extra effort required by double mapping?
RichardOD
I don't like the the idea of adding ssn and totalDrWhoEpisodesWatched to the constructor. Adding additional parameters that aren't actually needed in the running program just to perform testing seems counter to good API design, because it means making public a method that should not be used by consumers(other than a test consumer). What do you mean by a "mapped object"?
John
@RichardOD, sometimes :-)@John, by mapped object, I mean to use an object that you have coded yourself (instead of relying on the autogenerated class that linq2sql provides). Thus, when you select from your data store, you would do (select new MyPerson() { ... }) and set your properties there. This lets you avoid reliance on an autogenerated class that may or may not change in the future.
Joel Martinez
Joel. Thanks, I'll look at that.
John
+1  A: 

In .NET, you could mark your setters as "internal" and use the InternalsVisibleTo assembly attribute to make internals visible to your test assembly. That way your setters won't be public, but you can still access them.

note: even though the question isn't tagged .NET, I assumed it was based on your usage of object initializer syntax. If my assumption was wrong, this suggestion does not apply (unless the language you're using has an equivalent feature, of course).

dss539
I didn't mark it .Net, however I figured that the mentions of ASP.net and LinqToSQL would give that away. Thanks.
John
A: 

If it's for tests - consider using reflection. That wouldn't involve messing around your domain model.

For example - i got FactoryBase class, which uses reflection to set needed prop by lambda expression through parameters (like this). Works like a charm - creating new factory is simple as defining repository type and default entity data.

Arnis L.
The answer is intriguing, but I don't see how it solves the problem that I asked about, which is how to handle read-only properties in fakes. Foo has setters for all 3 properties (Bar, Baz and Bling). What would you do if BAZ only had a getter?
John
Reflection can handle that. Got some domain objects with protected and private setters too.
Arnis L.
+4  A: 

Consider mocking the Person type in your test. Example using Moq:

var mock = new Mock<Person>();
mock.SetupGet(p => p.id).Returns("EMP12345");
mock.SetupGet(p => p.ssn).Returns("123-45-6789");
mock.SetupGet(p => p.totalDrWhoEpisodesWatched).Returns(42);
return mock.Object;

Otherwise, try finding out how LINQ to SQL sets those read only properties.

EDIT: If you attempt the above and Moq throws an ArgumentException in the SetupGet call with the message "Invalid setup on a non-overridable member: p => p.id", then you need to mark the property as virtual. This will need to be done for each property whose getter you wish to override.

In LINQ to SQL, this can be done in the OR designer by selecting the property, then in the Properties window set Inheritance Modifier to virtual.

mvr
Can moq bypass readonly props? I remember that i had problems with Rhino mocks. :/
Arnis L.
Are you referring to readonly "fields", or properties without publicly accessible setters? In the former case I don't think Moq can help. In the latter case, the setters don't matter - Moq just overrides the getter.
mvr
Read-only properties(i.e. properties with set). Linq to SQL sets the private variable in the Person class, which in this example would be _id, _ssn and _totalDrWhoEpisodesWatched.
John
mvr, is this a standard feature of mock frameworks or is this particular to Moq?
John
I don't have experience using other mock frameworks, so I wouldn't be able to answer that I'm afraid.
mvr
A: 

I also use Moq. I love it and it works great. But, before I started using Moq, I wrote many fakes. Here's how I would have solved the problem using fakes.

Since a fake can have additional methods that the "production" implementation doesn't have, I would add a few extra methods to my fake implementation to handle setting the read-only portion.

Like this:

public class FakePersonRepository : IPersonRespository
{
    private IDictionary<int, Person> _people = new Dictionary<int, Person>();

    public Person GetPerson(int id)  // Interface Implementation
    {
        return _people(id);
    }

    public void SetPerson(int id, Person person)  // Not part of interface
    {
         _people.Add(id, person);
    }

}
magnifico