AnswerHow many tests?
Two days ago I asked how many tests this method need:
///<summary> ///Get the latest published webcast ///</summary> public Webcast GetLatest();
Here is what I came up with:
[TestFixture] public class WebcastRepositoryTest : DatabaseTestFixtureBase { private IWebcastRepository webcastRepository; [TestFixtureSetUp] public void TestFixtureSetup() { IntializeNHibernateAndIoC(PersistenceFramework.ActiveRecord, "windsor.boo", MappingInfo.FromAssemblyContaining<Webcast>()); } [SetUp] public void Setup() { CurrentContext.CreateUnitOfWork(); webcastRepository = IoC.Resolve<IWebcastRepository>(); } [TearDown] public void Teardown() { CurrentContext.DisposeUnitOfWork(); } [Test] public void Can_save_webcast() { var webcast = new Webcast { Name = "test", PublishDate = null }; With.Transaction(() => webcastRepository.Save(webcast)); Assert.AreNotEqual(0, webcast.Id); } [Test] public void Can_load_webcast() { var webcast = new Webcast { Name = "test", PublishDate = null }; With.Transaction(() => webcastRepository.Save(webcast)); UnitOfWork.CurrentSession.Evict(webcast); var webcast2 = webcastRepository.Get(webcast.Id); Assert.AreEqual(webcast.Id, webcast2.Id); Assert.AreEqual("test", webcast2.Name); Assert.IsNull(webcast2.PublishDate); } [Test] public void When_asking_for_latest_webcast_will_not_consider_any_that_is_not_published() { var webcast = new Webcast { Name = "test", PublishDate = null }; With.Transaction(() => webcastRepository.Save(webcast)); Assert.IsNull(webcastRepository.GetLatest()); } [Test] public void When_asking_for_latest_webcast_will_get_published_webcast() { var webcast = new Webcast { Name = "test", PublishDate = null }; With.Transaction(() => webcastRepository.Save(webcast)); var webcast2 = new Webcast { Name = "test", PublishDate = DateTime.Now.AddDays(-1) }; With.Transaction(() => webcastRepository.Save(webcast2)); Assert.AreEqual(webcast2.Id, webcastRepository.GetLatest().Id); } [Test] public void When_asking_for_latest_webcast_will_get_the_latest_webcast() { var webcast = new Webcast { Name = "test", PublishDate = DateTime.Now.AddDays(-2) }; With.Transaction(() => webcastRepository.Save(webcast)); var webcast2 = new Webcast { Name = "test", PublishDate = DateTime.Now.AddDays(-1) }; With.Transaction(() => webcastRepository.Save(webcast2)); Assert.AreEqual(webcast2.Id, webcastRepository.GetLatest().Id); } [Test] public void When_asking_for_latest_webcast_will_not_consider_webcasts_published_in_the_future() { var webcast = new Webcast { Name = "test", PublishDate = DateTime.Now.AddDays(-2) }; With.Transaction(() => webcastRepository.Save(webcast)); var webcast2 = new Webcast { Name = "test", PublishDate = DateTime.Now.AddDays(2) }; With.Transaction(() => webcastRepository.Save(webcast2)); Assert.AreEqual(webcast.Id, webcastRepository.GetLatest().Id); } }
And the implementation:
public class WebcastRepository : RepositoryDecorator<Webcast>, IWebcastRepository { public WebcastRepository(IRepository<Webcast> repository) { Inner = repository; } public Webcast GetLatest() { var publishedWebcastsByDateDesc = from webcast in Webcasts where webcast.PublishDate != null && webcast.PublishDate < SystemTime.Now() orderby webcast.PublishDate descending select webcast; return publishedWebcastsByDateDesc.FirstOrDefault(); } private static IOrderedQueryable<Webcast> Webcasts { get { return UnitOfWork.CurrentSession.Linq<Webcast>(); } } }
I think it is pretty sweet.
More posts in "Answer" series:
- (05 Jan 2023) what does this code print?
- (15 Dec 2022) What does this code print?
- (07 Apr 2022) Why is this code broken?
- (20 Jan 2017) What does this code do?
- (16 Aug 2011) Modifying execution approaches
- (30 Apr 2011) Stopping the leaks
- (24 Dec 2010) This code should never hit production
- (21 Dec 2010) Your own ThreadLocal
- (11 Feb 2010) Debugging a resource leak
- (03 Sep 2009) The lazy loaded inheritance many to one association OR/M conundrum
- (04 Sep 2008) Don't stop with the first DSL abstraction
- (12 Jun 2008) How many tests?
Comments
I said 7, I was the closest I think.
I am interested in why you haven't tested the default when there are no Webcasts in the repository? I can see that your GetLatesMethod has a FirstOrDefault() does the method for this test cover this scenario?
First or default would return null, yes.
I actually forgot about this
Hm, thinking about it, the test that test for no published results is the same, the code doesn't know if there are unpublished webcasts, after all
Don't know that it should appear in your tests but the only other concern would be issues related to not using UTC dates when working with 'Latest' data.
That is why I have SystemTime.Now()
Forgive my ignorance. I"ve following your blog after learning how nice Rhino Mocks was. Could explain this line of code? I'm unfamiliar with much of what's going on here?
With.Transaction(() => webcastRepository.Save(webcast));
I assume With is one of the Rhino.Common classes? What is "() => " doing? I've never seen that syntax in C#.
Thanks
This is part of Rhino Commons, it is a way to run under a transaction.
the ()=> is a labmda, a C# 3.0 concept
looks like you're using NHibernate.Linq. what is the state of the project and would you also use it by now in a real project? When there will be the first release?
I am using it, it is working well enough for most common scenarios.
We are still missing some parts in the more complex queries, but we are working on that.
How do you register WebcastRepository in your Windsor config?
Because the class implements two interfaces (IRepository<Webcast> and IWebcastRepository for the "special" methods) it actually implements two services.
Do you register two services for the class? (Not sure if this is possible, though.)
From the application/controller point of view, when do you use
a) Repository<Webcast>.SomeMethod()
b) IoC.Resolve(IWebcastRepository).SomeMethod()
c) IoC.Resolve(WebcastRepository).SomeMethod()
?
Thanks,
Alex
Here is my full configuration file:
import Castle.MonoRail.Framework
import Castle.MonoRail.WindsorExtension
import Rhino.Commons.Facilities from Rhino.Commons.ActiveRecord
import file from Configuration.boo
facility MonoRailFacility
facility RhinoTransactionFacility
facility ActiveRecordUnitOfWorkFacility:
for type in AllTypesBased of IController("HibernatingRhinos"):
for type in AllTypesBased of ViewComponent("HibernatingRhinos"):
fromHibernatingRhinosServices = {t as System.Type | t.Namespace == "HibernatingRhinos.Services"}
for type in AllTypes("HibernatingRhinos") \
SetupConfigurationProperties()
My question stems from the following use case: Say you're overriding methods of RepositoryDecorator<T> in a derived class to have further validation happening before a save/insert takes place.
I would want users of the repository make "fall into the pit of success" (via Scott Hanselman) such that calls to Repository<T>.Save are forced through the custom repo. The problem I see is that you now have
a) a custom repo that should be invoked when Repository<T> is used
b) an extended repo with methods like GetLatest()
both implemented by one class, but accessible through two Windsor service interfaces.
From an application point of view you could now use one of the three APIs above (a, b, c). Which one do you prefer?
Alex
In this case, I don't have different impl, so I don't care.
If I would, I would register WebcastRepository twice, once for IWebcastRepository and the second for IRepository<Webcast>
Sounds reasonable. That's also what I did, although it makes the registration a bit more complicated.
It seems that the size of the test class compared to the size of the implementation class is going to frighten people. See http://www.dotnetguru.org/modules.php?op=modload&name=News&file=article&sid=1090 (in french).
I think you could underline that some of the code in the test class is not related to unitary testing GetLatest(). Many lines setup and test the persistance layer (can_save, can_load). Shouldn't we assume that the persistance layer (the Inner property) is tested elsewhere by another set of unit tests ?
Comment preview