Hi @brakowski -
The least test-friendly aspect of DevForce is our lack of documentation on the subject. We have yet to describe how you can effectively and efficiently test your DevForce application. Explanations, samples, videos ... they're on our roadmap.
But the facts are: we make it easy to automate testing of DevForce applications - integration as well as unit testing.
Obviously testability is a big topic and would require far more space than I have for this reply. I'll offer some suggestions and point you to some examples along the way. Perhaps they will begin to change your opinion.
You are correct that many DevForce components do not expose interfaces that cover their public APIs. Many components expose abstract classes instead of interfaces and many methods you wish were virtual are not. This can be off-putting but it's not arbitrary. They are consequences of decisions we made in favor of making a stable and supportable commercial product. Those benefits come with trade-offs ... but this is a longer discussion for another time.
The good news is that, as a practical matter you don't always need these architectural artifacts. They are a means to an end. "Testing" is the end. Thanks to specific test-oriented features of DevForce, you can automate testing pretty easily once you know how.
Here are some brief clues:
Test your entities independently of DevForce components. You can "new" them and exercise your custom business logic with free-standing entity objects. While your entities do derive from our Entity base class, we don't impose a constructor on you and they work fine on their own ... until you engage DevForce related logic such as navigations, validations and property interception ... at which point ...
Add test entities to an offline "Test" EntityManager.
1. Create a test EntityManager
var testEm = new MyEntityManager(shouldConnect: false);
2. "New" up some entities, including related entities, and add them to the testEm
var testCustomer = new Customer {Name = "Galactic Wonder"};
testEm.AddEntity(testCustomer);
testCustomer.Orders.Add(new Order {OrderDate = DateTime.Now}); // Add orders to customer
testCustomer.Orders.Add(new Order {OrderDate = new DateTime(2011, 1, 1)});
testEm.AcceptChanges(); // Added entities are now "Unchanged"; simulating result of a query.
Now you're ready to play with a cache of test entities, configured as you need them. Navigation, validation, property interception work.
You can invoke queries on the testEm as if you expected them to go to the database. They'll run cache-only (unless you explicitly set their QueryStrategies) because an offline EntityManager queries and navigates cache-only.
Typically, this behavior will be transparent to the application components you're trying to test ... the ones that might be manipulating a Customer or issuing a query.
Use the Repository pattern. I like my application components to delegate all persistence operations to a client-side "repository" or "data service" that encapsulates the dirty details. My repositories have an interface which makes them easy to fake. My fake repositories often leverage an offline EntityManager (like testEm above) so that I get the effect of an in-memory database for free.
I use the repository to fake "saves" too. When the repository receives a save request, it manipulates the state of the cache to reflect the state you want the cache and entities to have in your test. For example, you could tweak some entity values and call "AcceptChanges()". You could throw an exception that appeared to have come from the server.
Note that throughout this process we've been working offline. We're not contacting the server at all! We don't even need a database.
This being Silverlight, your repostitory APIs must be structured as async calls. That usually makes testing hard. Not when you're using an offline test EntityManager. An EntityManager will return async calls synchronously when (a) it's offline or (b) it can satisfy the request entirely from cache.
I really like this offline approach because it means I don't have to engage the async aspects of the Silverlight Unit Test framework. That framework and the async testing style it imposes is painful.
It's also unavoidable when you're doing integration tests that touch the server. I strive to keep most of my tests local, offline, and synchronous. They're faster, cleaner and more robust than tests which depend upon database access ... especially async database access.
Maybe you know about it already? In which case, you know about all that "Enqueue ..." jazz.
We've wrapped that in our test framework which rides on top of the Silverlight Unit Test Framework . Our wrapper framework tries to simplify the process AND also enables async testing of both Silverlight and regular .NET code. Yup ... you may want your WPF and Windows Forms clients to be async too. Our test harness can run the same async tests in Silverlight AND full .NET WITHOUT changing you test code.
That's also a good place to see a variety of query testing patterns. Here's one example:
[TestMethod, Asynchronous, Timeout(20000)]
[Tag("Integration"), Tag("EasyQuery")]
public void then_get_customers_with_Model_EntityManager() {
DoItAsync(() =>
Manager
.Customers // "Get all customers" query
.ExecuteAsync()
.TestTheResults(
custs => custs.ShouldNotBeEmpty("Should have customers"),
"Customers query")
);
}
Fake Backing Store for (almost) end-to-end testing. Sometimes you want tests that exercise the path from the client, over the network, to the server and back. The only thing you DONT want to do is touch a database. Maybe you don't want to avoid database changes after the test (what a pain to unwind ... especially if the test fails midway). Perhaps you want to pre-fill the "database" with test data that's just for this one test only. Perhaps you don't even have a database.
That' s also the place to learn about faking the LoginManager and faking other MEF-injected components.
The Base Operation and derivatives are not mockable
You're the second person to complain about that; I am the first :D.
Seriously, I tried to get that changed ... and I think it would be nice ... but when asked to prove that I really needed it ... I haven't been able to do that. I've always found an easy way to accomplish my testing goal, even if it wasnt' the first way I thought of.
Maybe you'll be the one with the compelling use case.
I don't understand what you're asking about "StateManager, CompositionContext". We don't have a "StateManager". Will you please explain what it is you wanted to test?
Conclusion:
I hope this post gets you started. I trust it shows we've given testing serious thought and dedication.
Our professional services team helps many of our customers write their real-world business applications and we do a lot of automated testing (unit and integration testing) for them.
So ... yes ... we know from long experience how easily you can test a DevForce application.
I am sure there are some areas that are hard to test. Maybe you've found one. We'd love to know about it.
Concrete examples are always critical. We could sit here all day and dream up things that might happen ... ways we might want to test something. But when we don't have a real use case, we leave such speculation in dreamland. Harsh experience has taught us: you don't get real work done supporting speculative cases.
If you're stuck, please give us something concrete that you need to test ... a small, clear example ... and we'll be happy to help you through it. We might learn something too that will help us improve the product for you ... and everyone.
Cheers!
Personal note: I'm out of the country for the next three weeks. Other folks at IdeaBlade may be able to answer your follow up questions on this subject in the interim.
Edited by WardBell - 09-Aug-2011 at 4:10pm