Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Model Center   Code  
»áÔ±   
   
 
     
   
 ¶©ÔÄ
  ¾èÖú
µ¥Ôª²âÊԺͼ¯³É²âÊÔÒµÎñÓ¦ÓóÌÐò
 
×÷ÕߣºOmar Al Zabir À´Ô´£ºwww.codeproject.com ·¢²¼ÓÚ£º 2017-6-2
  3566  次浏览      28
 

±¾ÎÄÖ÷Ҫͨ¹ýСÀý×Ó£¬½éÉÜϵ¥Ôª²âÊÔ¡¢¼¯³É²âÊÔ¡¢²âÊÔÇý¶¯¿ª·¢µÈÏà¹Ø¸ÅÄî¡£

µ¥Ôª²âÊÔÊÇͨ¹ýд´úÂëÀ´²âÊÔ´úÂëµÄÒ»¸öСµ¥Î»²âÊÔ·½Ê½¡£ÔÚÒ»¸öµ¥Ôª²âÊÔÖУ¬Ò»´ÎÖ»ÄܲâÊÔÒ»¸öÀà¡£ÀýÈ磬Èç¹ûÄãÕýÔÚ²âÊÔÒ»¸öʹÓÃÀàFile£¬¶øÁíÒ»¸ö²âÊÔÀàDummyFileÊÇÓÃÀ´Ïû³ý¶ÔÎïÀíÎļþϵͳµÄÐèÒª£¬µ¥Ôª²âÊԵIJâÊÔÀàÂß¼­Ó¦¸Ã±»²âÊÔ¡£ÎªÁËʹclassʵÏÖ´ÓÕæÊµµ½Ðé/´æ¸ùÖ®¼äÇл»£¬½Ó¿Ú´úÌæÕæÕýµÄÀàʹÓá£ËùÒÔ£¬ÄãµÄÀàӦʹÓÃIFile»òIDatabase¶ø²»ÊÇÖ±½ÓʹÓÃFileºÍDatabase¡£

¼¯³É²âÊÔ£¬Ò²½Ð×é×°²âÊÔ»òÁªºÏ²âÊÔ¡£ÔÚµ¥Ôª²âÊԵĻù´¡ÉÏ£¬½«ËùÓÐÄ£¿é°´ÕÕÉè¼ÆÒªÇó£¨Èç¸ù¾Ý½á¹¹Í¼£©×é×°³ÉΪ×Óϵͳ»òϵͳ£¬½øÐм¯³É²âÊÔ¡£ÀýÈ磺ÕýÔÚ²âÊÔCunsomerData£¬ËüÒÀÀµÓÚLINQ to SQLÊý¾Ý¿âÁ¬½Ó£¬ÔÚ¼¯³É²âÊÔÖУ¬È·±£ÄÜÕý³£µ÷¶¯ËùÓÐÏà¹ØÀàµÄ·½·¨¡£

²âÊÔÇý¶¯¿ª·¢£¨TDD£©Êǵ¥Ôª²âÊԵļ«¶ËÐÎʽ¡£×ܵÄÔ­ÔòÊÇÏÈдµ¥Ôª²âÊÔ£¬È»ºó±àдʵ¼ÊµÄ´úÂë¡£ÀýÈ磬ÏÈдµ¥Ôª²âÊÔ£¬²âÊÔCustomerDataÓëÔÚûÓÐÕæÕýµÄ´úÂëÀàCustomerData¡£¸ÃCustomerDataÀà¿ÉÄܰüº¬ÀàËÆ¹¦ÄÜInsertCustomer £¬DeleteCustomer £¬ GetCustomerµÈ£¬ËüÃÇ×öµÄ²»Íâºõ·µ»ØÒ»Ð©ÐéÄâCustomer¶ÔÏó£¬ÒÔÂú×ãµ¥Ôª²âÊÔ¡£ Ò»µ©µ¥Ôª²âÊÔ¶¼ÓëÐéÄâÊý¾ÝÏàÁ¬£¬È»ºóÄ㿪ʼдµÄCustomerDataʵ¼Ê´úÂ룬Ëü¾Í»á·ÃÎÊÊý¾Ý¿â×öÕæÕýµÄ´¦Àí¡£±àÐ´ÕæÕýµÄ´úÂëºó£¬µ¥Ôª²âÊÔ²¢Ã»Óиıä²âÊÔ´úÂë¡£TDDÒªÇóÀà±»Éè¼ÆÔÚûÓÐÖ±½ÓÒÀÀµÓÚÆäËûÀàÖС£ËùÓеÄÒÀÀµ¹ØÏµÍ¨¹ý½Ó¿Ú¡£ÀýÈ磬CustomerData²»Ö±½ÓʹÓÃSqlConnection£¬¶øÊÇʹÓÃISqlConnection £¬ËùÓеÄÒÀÀµÌṩ¸øCustomerData¹¹Ôì¡£

ʹÓÃÐÐΪÇý¶¯¿ª·¢²âÊÔ

²âÊԵĵ¥Ò»·½·¨ÓÃÓÚµ¥¸öÆÚÍûÊÇÂé·³µÄ¡£Äã±ØÐëд¸ü¶àµÄ²âÊÔ·½·¨À´²âÊÔÿ¸ö·½·¨µÄÕûÌåÐÐΪ¡£´ËÍ⣬ÔÚÿ¸ö²âÊÔ·½·¨ÖУ¬Äã±ØÐëÔÚÊʵ±»·¾³Ï½¨Á¢²âÊÔÀֻ࣬ÊÇΪÁËÑéÖ¤Ò»¸öÌØ¶¨µÄÆÚÍû·½Ê½¡£ ÀýÈ磺¸ø¶¨Ò»¸ö¿ÕStack µ±Ò»¸öÏîÄ¿±»Ñ¹Èë¶ÑÕ»ºÍPop±»³ÆÎª¶ÔÏóÈ»ºóѹÈë¶ÑÕ»ÖеÄ×îºóÒ»Ïî±»·µ»ØÊ±£¬¸ÃÏîÄ¿´Ó¶ÑÕ»ÖÐÒÆ³ý£¬ÒÔ¼°ÈκζîÍâµ÷ÓÃPop¶¼Å׳öÒì³£¡£ÕâÀﶨÒåÍêÕûÐÐΪµÄPop·½·¨¡£²âÊÔÕâÖÖÐÐΪ·½·¨µÄËùÓÐÔ¤ÆÚºÍÏà¹ØÐÐΪPop ¡£

ʹÓÃBDDµ¥Ôª²âÊÔ

ÔÚµÚÒ»¸öÀý×ÓÖУ¬ÎÒÃǽ«½øÐе¥Ôª²âÊÔÊý¾Ý·ÃÎʲ㡣ʹÓÃLINQµ½SQL¶ÔÏó³Ö¾Ã»¯Êý¾Ý·ÃÎʲ㽻Ò×»º´æÔÚʵÌå²ãÃæ¡£ÀýÈ磬µ±ÄãÒª¼ÓÔØÒ»¸öÓû§£¬ËüÊ×Ïȼì²é¸ßËÙ»º´æ£¬¿´¿´Óû§ÊÇ·ñÒѾ­»º´æ£¬Èç¹ûûÓУ¬Ëü´ÓÊý¾Ý¿âÖмÓÔØÓû§£¬È»ºó»º´æËü¡£ÎÒÃÇÀ´¿´¿´PageRepository £¬ÆäÖÐËùÉæ¼°PageʵÌå³Ö¾ÃÐÔ¡£¹²Í¬´´½¨£¬¶ÁÈ¡£¬¸üкÍÏú»Ù£¨CRUD£©·½·¨¡£¾ÙÒ»¸öÀý×Ó·½·¨GetPageById £¬ÐèÒªÒ»¸öPageId²¢¼ÓÔØ¸ÃPage´ÓÊý¾Ý¿âÖС£

public class PageRepository : IPageRepository
{
#region Fields
private readonly IDropthingsDataContext _database;
private readonly ICache _cacheResolver;
#endregion Fields

#region Constructors
public PageRepository(IDropthingsDataContext database, ICache cacheResolver)
{
this._database = database;
this._cacheResolver = cacheResolver;
}
#endregion Constructors

#region Methods
public Page GetPageById(int pageId)
{
string cacheKey = CacheSetup.CacheKeys.PageId(pageId);
object cachedPage = _cacheResolver.Get(cacheKey);

if (null == cachedPage)
{
var page = _database.GetSingle<Page, int>(
DropthingsDataContext.SubsystemEnum.Page,
pageId,
LinqQueries.CompiledQuery_GetPageById);
page.Detach();
_cacheResolver.Add(cacheKey, page);
return page;
}
else
{
return cachedPage as Page;
}
}
}

PageRepositoryÐèÒªIDropthingsDataContext £¬ÕâÊDzâÊÔÓëLINQ to SQLµÄÒ»¸öµ¥Î»DataContext ¡£Ä¬ÈÏÇé¿öÏ£¬LINQµ½SQL²»»áÉú³ÉDataContext¾ÍÊǵ¥Ôª²âÊÔ¡£Ä㽫²»µÃ²»³¢ÊÔ×öÒ»¸öDataContextµ¥Ôª²âÊÔ¡£½Ó×Å£¬ËüÐèÒªÒ»¸öICacheÆäÊÇÓ뻺´æÉæ¼°µÄ½Ó¿Ú¡£ÔÚÕâ¸öÀý×ÓÖУ¬¼ÙÉèÓÐÒ»¸öÃûΪÀàEnterpriseLibraryCacheËü½«ÊµÏÖICache ¡£

²âÊÔ¼°Ô¤ÆÚ½á¹ûÈ·±££º¼øÓÚÐÂPageRepositoryºÍÒ»¸ö¿ÕµÄ»º´æ£¬µ± GetPageByIdÊÇ´øÒ»¸öPageId ¡£ËüÊ×Ïȼì²é»º´æ£¬Èç¹û·¢ÏÖûÓУ¬Ëü´ÓÊý¾Ý¿âÖеÄÍøÒ³ÖмÓÔØ£¬²¢·µ»ØÔ¤ÆÚÒ³Ãæ¡£

public void GetPage_Should _Return_A_Page_ from_database _when_cache_is_empty_and_ then_caches_it ()
{
var cache = new Mock();
var database = new Mock();
IPageRepository pageRepository = new PageRepository (database.Object, cache.Object);
const int pageId = 1;
var page = default(Page);
var samplePage = new Page() { ID = pageId, Title = "Test Page", ...};
database
.Expect (d => d.GetSingle<Page, int> (
DropthingsDataContext.SubsystemEnum.Page,
1, LinqQueries.CompiledQuery_GetPageById))
.Returns(samplePage);
"Given PageRepository and empty cache".Context(() =>
{
// cache is empty
cache.Expect (c => c.Get(It.IsAny())) .Returns (default(object));
// It will cache the Page object afte loading from database
cache.Expect(c =>
c.Add(It.Is(cacheKey =>
cacheKey == CacheSetup.CacheKeys.PageId(pageId)),
It.Is(cachePage =>
object.ReferenceEquals(cachePage, samplePage))))
.AtMostOnce().Verifiable();
});
"when GetPageById is called".Do(() =>
page = pageRepository.GetPageById(1));
"it checks in the cache first and finds nothing and then caches it" .Assert(() =>
cache.VerifyAll());
"it loads the page from database".Assert(() =>
database.VerifyAll());
"it returns the page as expected".Assert(() =>
{
Assert.Equal(pageId, page.ID);
});
}

µ¥Ôª²âÊÔµÄÒâÒåºÎÔÚ?

ÎÒ¾õµÃдµ¥Ôª²âÊÔʱ£¬Ëù²âÊԵķ½·¨²»Ö»ÊÇÔÚµ÷ÓòâÊÔ·½·¨¡£µ¥Ôª²âÊÔÒѾ­È·ÇеØÖªµÀʲôÆäËüµÄÀàºÍ·½·¨½«±»µ÷Óá£ÔÚÉÏÃæµÄÀý×ÓÖУ¬ÊÇ·ñʹÓÃcache»òdatabaseÊÇÔÚ·½·¨Öоö¶¨µÄ£¬ËùÒÔ£¬¿ÉÒÔ½øÐÐÂß¼­²âÊÔ¡£ÀýÈ磬ÎҸıäÁË´úÂëÀ´Ê¹ÓÃAspectF¿â¡£ÕâÐèÒª´úÂë±ä¸üPageRepository ¡£¸ü¸Ä´úÂëºó£¬ÎÒÐèҪȷ±£PageRepository»¹Êǰ´ÕÕÔ¤ÆÚµÄÐÐΪ¡£²»¹ÜÎÒÓÃʲô·½·¨µÄ»º´æ£¬Ëü²»Ó¦¸Ã¸Ä±ä»º´æÐÐΪ£º¼ì²é»º´æ£¬ÒÔÈ·±£ËùÇëÇóµÄ¶ÔÏóÊDz»ÊÇÒѾ­ÔÚ»º´æÖУ¬È»ºó´ÓÊý¾Ý¿âÖмÓÔØ²¢»º´æËü¡£¸Ä±ä·½·¨GetPageById£¬ÊµÊ©ºóAspectF £¬ÈçÏÂËùʾ£º

public Page GetPageById(int pageId)
{
return AspectF.Define
.Cache(_cacheResolver, CacheSetup.CacheKeys.PageId(pageId))
.Return(() =>
_database.GetSingle<Page, int>(DropthingsDataContext.SubsystemEnum.Page,
pageId, LinqQueries.CompiledQuery_GetPageById).Detach());
}

ÏÖÔÚ£¬µ±ÎÒÔËÐе¥Ôª²âÊÔ£¬Ëü±íʾͨ¹ý¡£

ËüÈ·ÈÏÐÐΪPageRepositoryûÓиı䣬¾¡¹ÜËüµÄ´úÂë¼±¾ç±ä»¯¡£ÓÐÁËÕýÈ·µÄµ¥Ôª²âÊÔ£¬¼´Ê¹ÄãÔÚ´úÂëÖиıäÁË£¬Ö»ÒªÄãµÄµ¥Ôª²âÊÔÈ«²¿Í¨¹ý£¬ÄãµÄϵͳÊÇûÓÐÎÊÌâ¡£½ÓÏÂÀ´ÈÃÎÒÃÇÀ´²âÊÔ£¬µ±»º´æÂúÁË£¬ËüÕýÈ·µØ´Ó»º´æÖзµ»ØÒ»¸ö¶ÔÏ󣬶ø²»ÊDz»±ØÒªµÄ²éѯÊý¾Ý¿â¡£ÏÂÃæµÄÊÔÑ齫ȷ±££º

[Specification]
public void GetPage_Should_Return_A_Page_from _cache_when_it_is_already_cached()
{
var cache = new Mock();
var database = new Mock();
IPageRepository pageRepository = new PageRepository (database.Object, cache.Object);
const int pageId = 1;
var page = default(Page);
var samplePage = new Page() { ID = pageId, Title = "Test Page",
ColumnCount = 3 , LayoutType = 3 , UserId = Guid.Empty, VersionNo = 1,
PageType = Enumerations.PageTypeEnum .PersonalPage,
CreatedDate = DateTime.Now };
"Given PageRepository and the requested page in cache".Context(() =>
{
cache.Expect (c => c.Get (CacheSetup.CacheKeys.PageId (samplePage.ID)))
.Returns(samplePage);
});
" when GetPageById is called".Do(() =>
page = pageRepository.GetPageById (1));
"it checks in the cache first and finds the object is in cache".Assert(() =>
{
cache.VerifyAll();
});
"it returns the page as expected".Assert(() =>
{
Assert.Equal(pageId, page.ID);
});
}

Õâ¸öÊÔÑéÊǺܼòµ¥µÄ¡£Î¨Ò»µÄÇø±ðÊÇÔÚÉèÖÃContext £¬ÎÒÃÇÉ趨һ¸öÆÚÍû£¬´Ó»º´æÇëÇóÌØ¶¨µÄÒ³ÃæÊ±£¬Ëü½«·µ»ØsamplePage¶ÔÏó¡£Ö»ÒªÆäÖÐÈκα»µ÷Óú¯ÊýÖÐÓÐûÓÐÆÚÍûÉèÖã¬Mock½«Å׳öÒ»¸öÒì³£¡£Èç¹û´úÂëÊÔͼµ÷ÓÃÈκÎdatabase¶ÔÏó»òÈκζ«Î÷ÉÏµÄÆäËûcache¶ÔÏóʱ£¬Ëü»áÅ׳öÒ»¸öÒì³££¬´Ó¶ø±íÃ÷ËüûÓÐ×öʲô²»Ó¦¸Ã×öµÄ¡£

¼¯³É²âÊÔʹÓÃBDD

¼¯³É²âÊÔÒâζ×ÅÄãÒª²âÊÔµÄһЩÀ࣬ËüÓëÆäËüÀàºÍ»ù´¡ÉèÊ©¼¯³É£¬ÈçÊý¾Ý¿â£¬Îļþϵͳ£¬Óʼþ·þÎñÆ÷µÈ£¬µ±Äãдһ¸ö¼¯³É²âÊÔ£¬²âÊÔ×é¼þµÄÐÐΪӦ¸ÃÊÇûÓÐÈκÎʵÎïÄ£ÐÍ¡£´ËÍ⣬ËüÃÇÌṩ¶îÍâµÄÐÅÐÄ´úÂ빤×÷£¬ÒòΪËùÓбØÐèµÄ×é¼þºÍÒÀÀµ¹ØÏµÒ²±»²âÊÔ¡£

ÈçºÎ²âÊÔÒµÎñÍâ¹Û²ã£¬ÒµÎñÍâ¹Û´¦ÀíÊý¾Ý·ÃÎÊ×é¼þºÍËùÓÐÆäËûʵÓóÌÐò×é¼þµÄ±àÅÅ¡£Ëü·â×°ÁËÓû§²Ù×÷ΪһÌåµÄÉÌÒµÔË×÷¡£ÀýÈ磬ÔÚDropthings £¬µ±µÚÒ»´ÎȫеÄÓû§·ÃÎÊ£¬Óû§»ñµÃ´´½¨Ä¬ÈϵÄÒ³ÃæºÍ´°¿ÚС²¿¼þ¡£ÕâÐ©Ò³ÃæºÍС²¿¼þÀ´×ÔÒ»¸öÄ£°å¡£ÓÐÒ»¸öÃûΪanon_user@dropthings.comµÄÓû§ÓµÓÐĬÈϵÄÒ³ÃæºÍ´°¿ÚС²¿¼þ¡£Ìض¨Óû§µÄÒ³ÃæºÍ´°¿ÚС²¿¼þ±»¸´ÖƵ½Ã¿Ò»¸öÐÂÓû§ÖС£ÓÉÓÚÕâÊÇÒ»¸ö¸´ÔӵIJÙ×÷£¬ÊʺÏ×ö×Ô¶¯»¯µÄ¼¯³É²âÊÔ¡£

µ±Óû§Ê״ηÃÎʸÃDefault.aspx£¬¸ÃFirstVisitHomePageÊǺôÓõFacade¡£Ëüͨ¹ýÒ»¸ö¸´ÔӵĹý³ÌÀ´¿Ë¡ģ°åÒ³Ãæ¡¢Ð¡²¿¼þºÍÉèÖÃĬÈÏÓû§ÉèÖõȼ¯³É²âÊÔ£¬½«È·±£Èç¹ûFirstVisitHomePage±»µ÷ÓòÎÊý±êʶһ¸öеÄÓû§·ÃÎʵÄÕ¾µã£¬ÄÇôËü½«·µ»Ø¿ÉÒÔ¶ÔÓû§´´½¨µÄĬÈÏÒ³ÃæºÍ²¿¼þµÄÒ»¸ö¶ÔÏó¡£Òò´Ë£º ÓÉÓÚ֮ǰ´ÓÀ´Ã»ÓÐË­·ÃÎʹý¸ÃÍøÕ¾µÄÄäÃûÓû§£¬ µ±Óû§µÚÒ»´Î·ÃÎÊ£¬ È»ºóÔÚ׼ȷµÄÁкÍλÖÃ×÷Ϊanon_userµÄÍøÒ³Ð´´½¨µÄÒ³Ãæ´´½¨µÄС²¿¼þ¡£

public class TestUserVisit
{
public TestUserVisit()
{
Facade.BootStrap();
}
/// <summary>
/// Ensure the first visit produces the pages and widgets defined in the template user
/// </summary>
[Specification]
public void First_visit_should_ create_same_pages_and _widgets_as_the _template_user()
{
MembershipHelper .UsingNewAnonUser ((profile) =>
{
using (var facade = new Facade (new AppContext (string.Empty, profile.UserName)))
{
UserSetup userVisitModel = null;

// Load the anonymous user pages and widgets
string anonUserName = facade.GetUserSettingTemplate()
.AnonUserSetting Template.UserName;
var anonPages = facade.GetPagesOfUser (facade.GetUserGuidFromUserName (anonUserName));

"Given anonymous user who has never visited the site before"
.Context(() => { });

"when the user visits for the first time".Do(() =>
{
userVisitModel = facade.FirstVisitHomePage (profile.UserName,
string.Empty, true, false);
});

"it creates widgets on the newly created page at exact columns and
positions as the anon user's pages".Assert(() =>
{
anonPages.Each(anonPage =>
{
var userPage = userVisitModel.UserPages.First(page =>
page.Title == anonPage.Title
&& page.OrderNo == anonPage.OrderNo
&& page.PageType == anonPage.PageType);

facade.GetColumnsInPage (anonPage.ID).Each(anonColumn =>
{
var userColumns = facade.GetColumnsInPage (userPage.ID);
var userColumn = userColumns.First (column =>
column.ColumnNo == anonColumn.ColumnNo);
var anonColumnWidgets =
facade.GetWidgetInstancesInZone WithWidget (anonColumn.WidgetZoneId);
var userColumnWidgets =
facade .GetWidgetInstancesInZoneWithWidget (userColumn.WidgetZoneId);
// Ensure the widgets from the anonymous user template's columns are
// in the same column and row.
anonColumnWidgets.Each (anonWidget =>
Assert.True (userColumnWidgets.Where (userWidget =>
userWidget.Title == anonWidget.Title
&& userWidget.Expanded == anonWidget.Expanded
&& userWidget.State == anonWidget.State
&& userWidget.Resized == anonWidget.Resized
&& userWidget.Height == anonWidget.Height
&& userWidget.OrderNo == anonWidget.OrderNo).Count() == 1));
});
});
});
}
});
}

ÐèÒª½øÒ»²½µÄ½âÊÍ£ºÎª´ÓÄ£°åÓû§·¢ÏÖÿ¸öÒ³ÃæÈ·±£ÐÂÓû§´ÓÄ£°åÓû§µÄÒ³ÃæµÄ²¿¼þ»ñµÃÍêȫһÑùµÄÒ³Ãæ¡£»ñµÃÀ´×ÔÐÂÓû§µÄÒ³ÃæµÄ´°¿ÚС²¿¼þ±È½Ïÿ¸ö²å¼þ¡£µ±ÔÚ×öÒµÎñ²ãµÄ±ä»¯¶ÔÓÚÿ¸ö²å¼þÈ·±£¾ßÓÐÏàͬµÄÃû³Æ£¬×´Ì¬£¬Î»ÖõȶÀÒ»ÎÞ¶þµÄ²¿¼þ£¬ÎÒ¿ÉÒÔÔËÐм¯³É²âÊÔ£¬ÒÔÈ·±£¹Ø¼ü¹¦ÄÜÊÇ·ñ°´Ô¤ÆÚ¹¤×÷Íê³É£¬¶øÇÒÔÚÕû¸öÒµÎñ²ãûÓÐÆÆËðÈκεط½¡£ ÎÒÓÃxunit.console.exeÉÏÔËÐеɲâÊÔ²âÊÔ²¢Éú³ÉÒ»¸ö²»´íhtml±¨¸æ£º

¸Ã±¨¸æÊ¹ÓÃÏÂÃæµÄÃüÁîÐвúÉú£º d:\xunit\xunit.console.exe d: \trunk\src\Dropthings.Business.Facade .Tests\bin\Debug\Dropthings.Business.Facade.Tests.dll /html FacadeTest.html

Äú¿ÉÒÔʹÓÃGUI xUnit:

ʹÓÃBDDµÄµ¥Ôª²âÊÔ²âÊÔÇý¶¯¿ª·¢ µ½Ä¿Ç°ÎªÖ¹£¬ÎÒÃÇÒѾ­Í¨¹ý´úÂë±àд²âÊÔ£¬µ«Èç¹ûÄãÏÈ´úÂë±àд²âÊÔÓйØÇý¶¯¿ª·¢£¿¼ÙÉèÎÒÃÇÒªÌí¼ÓÐÐΪ£º¸ø¶¨Ò»¸öPageRepository £¬µ± Insert±»µ÷ÓÃʱ£¬ËüÓ¦¸ÃÔÚÊý¾Ý¿âÖвåÈëÒ³Ãæ£¬Çå³ýÁ˵õ½µÄÐÂÒ³Ãæ£¬Óû§Ò³ÃæÈκλº´æ¼¯ºÏ£¬·µ»ØÐ²åÈëµÄÒ³Ãæ¡£ ±àд²âÊÔ´úÂ룺

[Specification]
public void InsertPage_should_insert_a_page _in_database_and_cache_it()
{
var cache = new Mock();
var database = new Mock();
IPageRepository pageRepository = new PageRepository(database.Object, cache.Object);
const int pageId = 1;

var page = default(Page);
var samplePage = new Page() { ID = pageId, Title = "Test Page", ColumnCount = 3,
LayoutType = 3, UserId = Guid.NewGuid(), VersionNo = 1,
PageType = Enumerations.PageTypeEnum .PersonalPage, CreatedDate = DateTime.Now };

database
.Expect(d = > d.Insert ( DropthingsDataContext .SubsystemEnum.Page,
It.IsAny()))
.Returns(samplePage);

"Given PageRepository" .Context(() =>
{
// It will clear items from cache
cache.Expect (c => c.Remove (CacheSetup.CacheKeys.PagesOfUser (samplePage.UserId)));
});

" when Insert is called".Do(() =>
page = pageRepository .Insert((newPage) =>
{
newPage.Title = samplePage.Title;
newPage.ColumnCount = samplePage.ColumnCount;
newPage.LayoutType = samplePage.LayoutType;
newPage.UserId = samplePage.UserId;
newPage.VersionNo = samplePage.VersionNo;
newPage.PageType = samplePage.PageType;
}));

("then it should insert the page in database" +
"and clear any cached collection of pages for the user who gets the new page" +
"and it returns the newly inserted page").Assert(() =>
{
database.VerifyAll();
cache.VerifyAll();
Assert.Equal(pageId, page.ID);
});
}

Ê×ÏÈ£¬ÎÒÃǽ«Ð´Ò»Ð©ÐéÄâ´úÂëPageRepository.Insert·½·¨£¬·µ»ØÒ»¸öеÄPage¡£ËüÓ¦¸Ã»áfail£¬ÒòΪËü²»Âú×ãĿǰÊý¾Ý¿â¶ÔÏóµÄÆÚÍû¼¯¡£Èç¹ûûÓÐʧ°Ü£¬Ôò±íÃ÷ÎÒÃǵIJâÊÔÊÇ´íÎóµÄ¡£

public Page Insert(Action populate)
{
return new Page();
}

ÔËÐйÊÕϲâÊÔ½á¹ûÈçÔ¤ÆÚ£º

TestCase ¡®Given PageRepository when InsertPage is called, then it should insert the page in databaseand clear any cached collection of pages for the user who gets the new pageand it returns the newly inserted page¡¯ failed: Moq.Mock VerificationException : The following expectations were not met: IDropthingsDataContext d => d.Insert(Page, null) at Moq.Mock`1.VerifyAll() PageRepositoryTest.cs(278,0): at Dropthings.DataAccess.UnitTest.PageRepository Test.<>c_DisplayClass35. <InsertPage_should_inserta _page_in_database _and_cache_it>b__34()

Õâ±íÃ÷£¬Ã»Óкô½Ðdatabase.Insert £¬ËùÒÔ²âÊÔʧ°Ü¡£ÎÒÃÇʵÏÖÁËTDDµÄµÚÒ»²½£¬ÕâÊÇдһ¸ö²âÊÔ²¢Ê¹Æäʧ°ÜÒÔÀ´µÄµÚÒ»ÆÚÍûûÓÐÕýÈ·×é¼þϼìÑé¡£ ÏÖÔÚÌí¼ÓÕæÕýµÄ´úÂ룺

public Page Insert(Action populate)
{
var newPage = _database.Insert(
DropthingsDataContext.SubsystemEnum.Page, populate);
RemoveUserPagesCollection(newPage.UserId);
return newPage.Detach();
}

ÏÖÔÚ£¬µ±µ¥Ôª²âÊÔÊǶԵÄPageRepository £¬Ëüͨ¹ýеIJâÊÔ£¬ÓëÒÔÍùµÄ²âÊÔÒ»Æð£º

ʹÓÃBDD½øÐм¯³É²âÊÔ²âÊÔÇý¶¯¿ª·¢

Èç¹ûÎÒÃÇÏëΪ¼¯³É²âÊÔ×öTDD£¿ÎÒÃÇÈçºÎÏÈд²âÊÔ´úÂ룬ȻºóдËüÓëÆäËû×é¼þ¼¯³ÉÒµÎñ²ãµÄ´úÂ룿 ÎÒÃÇÈçºÎΪWeb²ã×öTDD£¿·½·¨ÊÇÒ»ÑùµÄ£¬Ê×ÏÈÄã±àд²âÊÔ´úÂ룬¸ø³öÕýÈ·µÄÊäÈ룬²¢ÆÚÍû´ÓÖлîµÃÊä³ö£¬È»¶ø£¬¼¯³É²âÊÔ²»Ó¦¸ÃÖ»µ÷Óõ¥¶ÀÒ»¸öÒµÎñ²Ù×÷£¬ÒÔÈ·±£ËüÄÜÕý³£¹¤×÷¡£¼¯³É²âÊÔ»¹Ó¦¸ÃÈ·±£Ö´ÐÐÆäËü²Ù×÷ʱ³öÏÖÕýÈ·µÄÐÐΪ¡£ÀýÈ磬ÔÚ²âÊÔFirstVisitHomePage ʱ£¬ÆÚÍûµÄÊÇ£¬µÚÒ»´Î·ÃÎÊÖ®ºó£¬Óû§¾ßÓд´½¨µÄÕýÈ·Ò³Ãæ¡£²âÊÔ´úÂëͨ¹ý¼ì²é·µ»ØµÄ¶ÔÏóÄ£ÐÍÑéÖ¤ÕâÒ»µã£¬µ«Êµ¼ÊÇé¿öÊÇ£¬ÔÚµÚÒ»´Î·ÃÎʺ󣬸ù¾ÝÓû§µÄ·µ»Ø£¬ËûÃÇÓ¦¸Ã¿´µ½ÏàͬµÄ²¿¼þ£¬ÔÙ´ÎÈ·ÈϵÚÒ»ºÍ¸´Õï·µ»ØÏàͬµÄÊý¾Ý¡£

²âÊÔÈçÏÂ:

public void Revisit_should_load_the _pages_and_widgets_exactly_the_same()
{
MembershipHelper .UsingNewAnonUser ((profile) =>
{
using (var facade = ew Facade (new AppContext (string.Empty, profile.UserName)))
{
UserSetup userVisitModel = null;
UserSetup userRevisitModel = null;

"Given an anonymous user who visited first".Context(() =>
{
userVisitModel = facade.FirstVisitHomePage(profile.UserName, ...);
});

"when the same user visits again".Do(() =>
{
userRevisitModel = facade.RepeatVisitHomePage (profile.UserName, ...);
});

"it should load the exact same pages, column and
widgets as the first visit produced".Assert(() =>
{
userVisitModel.UserPages.Each (firstVisitPage =>
{
Assert.True (userRevisitModel.UserPages.Exists (page =>
page.ID == firstVisitPage.ID));
var revisitPage = userRevisitModel.UserPages.First (page =>
page.ID == firstVisitPage.ID);
var revisitPageColumns = facade.GetColumnsInPage (revisitPage.ID);
facade.GetColumnsInPage (firstVisitPage.ID) .Each(firstVisitColumn =>
{
var revisitColumn = revisitPageColumns.First (column =>
column.ID == firstVisitColumn.ID);
var firstVisitWidgets = facade
.GetWidgetInstancesInZone WithWidget (firstVisitColumn.WidgetZoneId);
var revisitWidgets = facade
.GetWidgetInstancesInZoneWithWidget (revisitColumn.WidgetZoneId);
firstVisitWidgets.Each (firstVisitWidget =>
Assert.True (revisitWidgets.Where(revisitWidget =>
revisitWidget.Id == firstVisitWidget.Id).Count() == 1));
});
});
});
}
});
}

×ö¼¯³É²âÊÔµÄÕýÈ··½·¨ÊDZàдµ¥Ôª²âÊԵĶÔÁ¢Ãæ¡£ÔÚµ¥Ôª²âÊÔÖУ¬ÕâÖÖ·½·¨ÊÇͨ¹ýµ÷ÓÃÒ»ÖÖ·½·¨ºÍ´æ¸ù¡£ÔÚ¼¯³É²âÊÔ£¬ÄãÓ¦¸Ã²âÊÔ²»½öÖ»ÓÐÒ»¸ö²Ù×÷£¬¶øÇÒ»¹Ö´ÐÐÆäËüÏà¹Ø²Ù×÷£¬ÒÔÈ·±£²âÊԵIJÙ×÷ȷʵÊÇËüÓ¦¸Ã×öµÄ¡£¸ÅÀ¨ÁË¿ÉÄܵIJâÊÔÓÃÀý·ÖΪÒÔÏÂÀà±ð£º

µ±²âÊÔ´´½¨ÐÂÊý¾Ý²Ù×÷£¨ÀýÈ磬ÔÚÊý¾Ý¿âÖвåÈëÐлòµ÷ÓÃWeb·þÎñÀ´´´½¨Ò»¸öʵÌ壩£¬±£Ö¤Á˲Ù×÷ͨ¹ýÊʵ±½øÐУº µ÷Óã¬Í¨¹ýÔٴζÁÈ¡¸ÃÐлòµ÷ÓÃÁíÒ»¸öWeb·þÎñ£¬ÒÔ»ñµÃ´´½¨µÄʵÌå¶ÁÈ¡Êý¾ÝµÈ²Ù×÷¡£Èç¹ûÊý¾ÝûÓб»ÕýÈ·²åÈ루ÀýÈ磬²åÈë×ÓÐУ©Ó¦¸Ãʧ°Ü¡£ ÕâÊÇÒ»¸ö»ý¼«µÄ²âÊÔ¡£

µ÷ÓÃÈç¹û²åÈë³É¹¦£¬ÀýÈçÔٴβåÈëͬһÐлá²úÉúÒ»¸öÎ¥·´Ô¼Êø£¬½«Ê§°ÜµÄÆäËü²Ù×÷¡£ÕâÊÇÒ»ÖÖÏû¼«µÄ¿¼Ñé¡£

µ±²âÊԵIJÙ×÷µÄ¸üÐÂÊý¾Ý£¨ÀýÈ磬¸üÐÂÊý¾Ý¿âÖеÄÐУ©£¬±£Ö¤Á˲Ù×÷µÄÊý¾Ý£¬Í¨¹ýÊʵ±¸üРµ÷ÓÃʹÓøüкóµÄÊý¾Ý£¬Èç¹ûûÓÐÕýÈ·µÄ¸üÐÂÊý¾Ý»áʧ°Ü£¬ÀýÈçÆäËüµÄ²Ù×÷ʹÓà¶î²»×ãµÄÕË»§Á½´ÎÁ¬ÐøµÄ»ã¿îºó¡£ÕâÊÇÒ»¸ö»ý¼«µÄ²âÊÔ¡£ Èç¹ûµ÷Óøüгɹ¦£¬½«ÊÇʧ°ÜµÄÆäËü²Ù×÷£¬ÀýÈç³¢ÊÔʹÓøüкóµÄÏàÍ¬ÖµÔ¼Êø³åÍ»ÔÚÊý¾Ý¿âÖвåÈëÐÂÐС£ÕâÊÇÒ»ÖÖÏû¼«µÄ¿¼Ñé¡£

µ±Äã²âÊÔɾ³ýһЩÊý¾ÝµÄ²Ù×÷£¬±£Ö¤Êʵ±µÄÊý¾Ýɾ³ý Èç¹ûµ÷ÓÃÊý¾Ý´æÔÚÀýÈçÖØÐ²åÈëͬһÐвúÉúÒ»¸öÎ¥·´Ô¼Êø½«Ê§°Ü£¬ÆäËüµÄ²Ù×÷¡£ ºô½Ð£¬Èç¹ûÊý¾Ý±»ÕýÈ·µØÉ¾³ý£¬ÀýÈç²åÈëµÄ×ÓÐÐÊDz»´æÔÚµÄÐУ¬½«ÊÇʧ°ÜµÄ²Ù×÷¡£

ÔÚ¼¯³É²âÊÔ×öÕý·´Á½·½ÃæµÄ²âÊÔ£¬¼´Ê¹ÄãÕýÔÚ×öµ¥Ôª²âÊÔ£¬ÒÔÈ·±£²âÊÔº­¸ÇÁËϵͳµÄËùÓÐÖ÷ÒªÐÐΪÊǺÜÖØÒªµÄ¡£¼¯³É²âÊÔµÄÒ»¸öºÃ´¦ÊÇÔÚ¼ÙÉèÄã×Ô¼ºµÄ´úÂëÒѾ­µ¥Ôª²âÊԵĻù´¡ÉèÊ©²»¿ÉÔ¤²âÐÔ³¬¹ý²âÊÔ×Ô¼ºµÄ´úÂë¡£ÖØÒªµÄÊǾ¡¿ÉÄܶàµÄ´ÓÕýºÍ¸ºÁ½·½Ã澡¿ÉÄܸ²¸ÇÒÔÅųý»ù´¡ÉèÊ©±äÁ¿¡£

 

   
3566 ´Îä¯ÀÀ       28
Ïà¹ØÎÄÕÂ

΢·þÎñ²âÊÔÖ®µ¥Ôª²âÊÔ
һƪͼÎÄ´øÄãÁ˽â°×ºÐ²âÊÔÓÃÀýÉè¼Æ·½·¨
È«ÃæµÄÖÊÁ¿±£ÕÏÌåϵ֮»Ø¹é²âÊÔ²ßÂÔ
È˹¤ÖÇÄÜ×Ô¶¯»¯²âÊÔ̽Ë÷
Ïà¹ØÎĵµ

×Ô¶¯»¯½Ó¿Ú²âÊÔʵ¼ù֮·
jenkins³ÖÐø¼¯³É²âÊÔ
ÐÔÄܲâÊÔÕï¶Ï·ÖÎöÓëÓÅ»¯
ÐÔÄܲâÊÔʵÀý
Ïà¹Ø¿Î³Ì

³ÖÐø¼¯³É²âÊÔ×î¼Ñʵ¼ù
×Ô¶¯»¯²âÊÔÌåϵ½¨ÉèÓë×î¼Ñʵ¼ù
²âÊԼܹ¹µÄ¹¹½¨ÓëÓ¦ÓÃʵ¼ù
DevOpsʱ´úµÄ²âÊÔ¼¼ÊõÓë×î¼Ñʵ¼ù