Adding the unit tests for ViewComponents in MusicStore

This commit is contained in:
Youngjune Hong 2015-04-23 17:44:10 -07:00
parent ff3ffd9348
commit 797f802906
8 changed files with 316 additions and 4 deletions

View File

@ -25,6 +25,13 @@ namespace MusicStore.Components
set;
}
[Activate]
public ISystemClock Clock
{
get;
set;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var latestAlbum = await Cache.GetOrSet("latestAlbum", async context =>
@ -36,11 +43,11 @@ namespace MusicStore.Components
return View(latestAlbum);
}
private Task<Album> GetLatestAlbum()
private async Task<Album> GetLatestAlbum()
{
var latestAlbum = DbContext.Albums
var latestAlbum = await DbContext.Albums
.OrderByDescending(a => a.Created)
.Where(a => (a.Created - DateTime.UtcNow).TotalDays <= 2)
.Where(a => (a.Created - Clock.UtcNow).TotalDays <= 2)
.FirstOrDefaultAsync();
return latestAlbum;

View File

@ -0,0 +1,16 @@
using System;
namespace MusicStore.Components
{
/// <summary>
/// Abstracts the system clock to facilitate testing.
/// </summary>
public interface ISystemClock
{
/// <summary>
/// Gets a DateTime object that is set to the current date and time on this computer,
/// expressed as the Coordinated Universal Time(UTC)
/// </summary>
DateTime UtcNow { get; }
}
}

View File

@ -0,0 +1,22 @@
using System;
namespace MusicStore.Components
{
/// <summary>
/// Provides access to the normal system clock.
/// </summary>
public class SystemClock : ISystemClock
{
/// <inheritdoc />
public DateTime UtcNow
{
get
{
// The clock measures whole seconds only, and truncates the milliseconds,
// because millisecond resolution is inconsistent among various underlying systems.
DateTime utcNow = DateTime.UtcNow;
return utcNow.AddMilliseconds(-utcNow.Millisecond);
}
}
}
}

View File

@ -147,7 +147,7 @@ namespace MusicStore.Models
return order.OrderId;
}
// We're using HttpContextBase to allow access to cookies.
// We're using HttpContextBase to allow access to sessions.
private string GetCartId(HttpContext context)
{
var cartId = context.Session.GetString("Session");

View File

@ -10,6 +10,7 @@ using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
using Microsoft.Framework.Runtime;
using MusicStore.Components;
using MusicStore.Models;
namespace MusicStore
@ -104,6 +105,9 @@ namespace MusicStore
services.AddCaching();
services.AddSession();
// Add the system clock service
services.AddSingleton<ISystemClock, SystemClock>();
// Configure Auth
services.Configure<AuthorizationOptions>(options =>
{

View File

@ -0,0 +1,103 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.Data.Entity;
using Microsoft.Framework.Caching.Memory;
using Microsoft.Framework.DependencyInjection;
using MusicStore.Models;
using Xunit;
namespace MusicStore.Components
{
public class AnnouncementComponentTest
{
private readonly IServiceProvider _serviceProvider;
public AnnouncementComponentTest()
{
var services = new ServiceCollection();
services.AddEntityFramework()
.AddInMemoryStore()
.AddDbContext<MusicStoreContext>();
_serviceProvider = services.BuildServiceProvider();
}
[Fact]
public async Task AnnouncementComponent_Returns_LatestAlbum()
{
// Arrange
var today = new DateTime(year: 2002, month: 10, day: 30);
var announcementComponent = new AnnouncementComponent()
{
DbContext = _serviceProvider.GetRequiredService<MusicStoreContext>(),
Cache = _serviceProvider.GetRequiredService<IMemoryCache>(),
Clock = new TestSystemClock() { UtcNow = today },
};
PopulateData(announcementComponent.DbContext, latestAlbumDate: today);
// Action
var result = await announcementComponent.InvokeAsync();
// Assert
Assert.NotNull(result);
var viewResult = Assert.IsType<ViewViewComponentResult>(result);
Assert.Null(viewResult.ViewName);
var albumResult = Assert.IsType<Album>(viewResult.ViewData.Model);
Assert.Equal(today, albumResult.Created.Date);
}
private static void PopulateData(DbContext context, DateTime latestAlbumDate)
{
var albums = TestAlbumDataProvider.GetAlbums(latestAlbumDate);
foreach (var album in albums)
{
context.Add(album);
}
context.SaveChanges();
}
private class TestAlbumDataProvider
{
public static Album[] GetAlbums(DateTime latestAlbumDate)
{
var generes = Enumerable.Range(1, 10).Select(n =>
new Genre()
{
GenreId = n,
Name = "Genre Name " + n,
}).ToArray();
var artists = Enumerable.Range(1, 10).Select(n =>
new Artist()
{
ArtistId = n + 1,
Name = "Artist Name " + n,
}).ToArray();
var albums = Enumerable.Range(1, 10).Select(n =>
new Album()
{
Artist = artists[n - 1],
ArtistId = n,
Genre = generes[n - 1],
GenreId = n,
Created = latestAlbumDate.AddDays(1 - n),
}).ToArray();
return albums;
}
}
private class TestSystemClock : ISystemClock
{
public DateTime UtcNow { get; set; }
}
}
}

View File

@ -0,0 +1,104 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Session;
using Microsoft.Framework.Caching.Distributed;
using Microsoft.Framework.Caching.Memory;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging.Testing;
using MusicStore.Models;
using Xunit;
namespace MusicStore.Components
{
public class CartSummaryComponentTest
{
private readonly IServiceProvider _serviceProvider;
public CartSummaryComponentTest()
{
var services = new ServiceCollection();
services.AddEntityFramework()
.AddInMemoryStore()
.AddDbContext<MusicStoreContext>();
_serviceProvider = services.BuildServiceProvider();
}
[Fact]
public async Task CartSummaryComponent_Returns_CartedItems()
{
// Arrange
var viewContext = new ViewContext()
{
HttpContext = new DefaultHttpContext()
};
// Session initialization
var cartId = "CartId_A";
var sessionFeature = new SessionFeature()
{
Session = CreateTestSession(),
};
viewContext.HttpContext.SetFeature<ISessionFeature>(sessionFeature);
viewContext.HttpContext.Session.SetString("Session", cartId);
// DbContext initialization
var dbContext = _serviceProvider.GetRequiredService<MusicStoreContext>();
PopulateData(dbContext, cartId, albumTitle: "AlbumA", itemCount: 10);
// CartSummaryComponent initialization
var cartSummaryComponent = new CartSummaryComponent()
{
DbContext = dbContext,
ViewContext = viewContext,
};
// Act
var result = await cartSummaryComponent.InvokeAsync();
// Assert
Assert.NotNull(result);
var viewResult = Assert.IsType<ViewViewComponentResult>(result);
Assert.Null(viewResult.ViewName);
Assert.Null(viewResult.ViewData.Model);
Assert.Equal(10, cartSummaryComponent.ViewBag.CartCount);
Assert.Equal("AlbumA", cartSummaryComponent.ViewBag.CartSummary);
}
private static ISession CreateTestSession()
{
return new DistributedSession(
new LocalCache(new MemoryCache(new MemoryCacheOptions())),
"sessionId_A",
idleTimeout: TimeSpan.MaxValue,
tryEstablishSession: () => true,
loggerFactory: new NullLoggerFactory(),
isNewSessionKey: true);
}
private static void PopulateData(MusicStoreContext context, string cartId, string albumTitle, int itemCount)
{
var album = new Album()
{
AlbumId = 1,
Title = albumTitle,
};
var cartItems = Enumerable.Range(1, itemCount).Select(n =>
new CartItem()
{
AlbumId = 1,
Album = album,
Count = 1,
CartId = cartId,
}).ToArray();
context.AddRange(cartItems);
context.SaveChanges();
}
}
}

View File

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.Framework.DependencyInjection;
using MusicStore.Models;
using Xunit;
namespace MusicStore.Components
{
public class GenreMenuComponentTest
{
private readonly IServiceProvider _serviceProvider;
public GenreMenuComponentTest()
{
var services = new ServiceCollection();
services.AddEntityFramework()
.AddInMemoryStore()
.AddDbContext<MusicStoreContext>();
_serviceProvider = services.BuildServiceProvider();
}
[Fact]
public async Task GenreMenuComponent_Returns_NineGenres()
{
// Arrange
var genreMenuComponent = new GenreMenuComponent()
{
DbContext = _serviceProvider.GetRequiredService<MusicStoreContext>(),
};
PopulateData(genreMenuComponent.DbContext);
// Act
var result = await genreMenuComponent.InvokeAsync();
// Assert
Assert.NotNull(result);
var viewResult = Assert.IsType<ViewViewComponentResult>(result);
Assert.Null(viewResult.ViewName);
var genreResult = Assert.IsType<List<Genre>>(viewResult.ViewData.Model);
Assert.Equal(9, genreResult.Count);
}
private static void PopulateData(MusicStoreContext context)
{
var genres = Enumerable.Range(1, 10).Select(n => new Genre());
context.AddRange(genres);
context.SaveChanges();
}
}
}