unit-testing - 单元测试 + 工作单元 + 存储库
问题描述
我不得不寻求帮助。我正在写博客。架构是这样的,有 IRepository、PostRepository 和 Unit OfWork。我刚开始熟悉单元测试,无法理解如何正确测试方法。我非常希望您帮助创建一个测试 - 返回博客中所有帖子的列表,其余的我将自己完成。以下是清单:
后控制器:
using namespace WebSite.Controllers
{
public class PostsController : Controller
{
readonly UnitOfWork unitOfWork;
public ActionResult Index()
{
var postList = unitOfWork.Post.GetList();
return View( postList );
}
public ActionResult Details( int? id )
{
if (id == null)
{
return new HttpStatusCodeResult( HttpStatusCode.BadRequest );
}
var post = unitOfWork.Post.Get( id );
if (post == null)
{
return HttpNotFound();
}
return View( post );
}
public ActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(
[Bind( Include =
"Id,Title,Description,Content,Category,PostedDateTime,ModifiedDateTime" )]
Post post, HttpPostedFileBase file )
{
post.Author = User.Identity.Name;
if (!ModelState.IsValid || file == null)
return View( post );
//attach the uploaded image to the object before saving to Database
post.ImageMimeType = "image / jpeg" /*image.ContentLength*/;
post.ImageData = new byte[file.ContentLength];
file.InputStream.Read( post.ImageData, 0, file.ContentLength );
//Save image to file
var filename = file.FileName;
var filePathOriginal = Server.MapPath( "~/Images/Original" );
var savedFileName = Path.Combine( filePathOriginal, filename );
file.SaveAs( savedFileName );
//Read image back from file and create thumbnail from it
var imageFile = Path.Combine( Server.MapPath( "~/Images/Original" ),
filename );
using (var srcImage = Image.FromFile( imageFile ))
using (var newImage = new Bitmap( 100, 100 ))
using (var graphics = Graphics.FromImage( newImage ))
using (var stream = new MemoryStream())
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode =
InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage( srcImage, new Rectangle( 0, 0, 100, 100 ) );
newImage.Save( stream, ImageFormat.Png );
var thumbNew = File( stream.ToArray(), "image/png" );
post.ImageThumbnail = thumbNew.FileContents;
}
post.PostedDateTime = DateTime.Now;
unitOfWork.Post.Create( post );
unitOfWork.Save();
return RedirectToAction( "Index", "Home" );
}
[HttpGet]
public ActionResult Edit( int? id )
{
if (id == null)
{
return new HttpStatusCodeResult( HttpStatusCode.BadRequest );
}
var post = unitOfWork.Post.Get( id );
if (post == null)
{
return HttpNotFound();
}
return View( post );
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(
Post post,
HttpPostedFileBase file )
{
if (!ModelState.IsValid)
return View( post );
if (file != null)
{
//attach the uploaded image to the object before saving to
Database
post.ImageMimeType = "image / jpeg" /*image.ContentLength*/;
post.ImageData = new byte[file.ContentLength];
file.InputStream.Read( post.ImageData, 0, file.ContentLength );
//Save image to file
var filename = file.FileName;
var filePathOriginal = Server.MapPath( "~/Images/Original" );
var savedFileName = Path.Combine( filePathOriginal, filename );
file.SaveAs( savedFileName );
//Read image back from file and create thumbnail from it
var imageFile = Path.Combine( Server.MapPath(
"~/Images/Original" ), filename );
using (var srcImage = Image.FromFile( imageFile ))
using (var newImage = new Bitmap( 350, 200 ))
using (var graphics = Graphics.FromImage( newImage ))
using (var stream = new MemoryStream())
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode =
InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage( srcImage, new Rectangle( 0, 0, 350, 200
) );
newImage.Save( stream, ImageFormat.Png );
var thumbNew = File( stream.ToArray(), "image/png" );
post.ImageThumbnail = thumbNew.FileContents;
}
}
unitOfWork.Post.Edit( post );
unitOfWork.Save();
return RedirectToAction( "Index", "Home" );
}
public ActionResult Delete( int? id )
{
if (id == null)
{
return new HttpStatusCodeResult( HttpStatusCode.BadRequest );
}
var post = unitOfWork.Post.Get( id );
if (post == null)
{
return HttpNotFound();
}
return View( post );
}
[HttpPost, ActionName( "Delete" )]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed( int id )
{
{
var post = unitOfWork.Post.Get( id );
unitOfWork.Post.Delete( post );
unitOfWork.Save();
return RedirectToAction( "Index" );
}
}
public FileContentResult GetThumbnailImage( int? id )
{
var post = unitOfWork.Post.Get( id );
if (post.ImageThumbnail != null)
{
return File( post.ImageThumbnail, post.ImageMimeType );
}
else if (post.ImageThumbnail == null && post.ImageData == null)
{
var imageFile = Path.Combine( Server.MapPath(
"~/Images/Original/NoImageAvailable.jpg" ) );
post.ImageMimeType = "image / jpeg";
using (var srcImage = Image.FromFile( imageFile ))
using (var newImage = new Bitmap( 350, 200 ))
using (var graphics = Graphics.FromImage( newImage ))
using (var stream = new MemoryStream())
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode =
InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage( srcImage, new Rectangle( 0, 0, 350, 200
) );
newImage.Save( stream, ImageFormat.Png );
var thumbNew = File( stream.ToArray(), "image/png" );
post.ImageThumbnail = thumbNew.FileContents;
}
return File( post.ImageThumbnail, post.ImageMimeType );
}
else
{
var imageFile = Path.Combine( Server.MapPath( "~/Images/Default"
), post.DefaultImageName );
using (var srcImage = Image.FromFile( imageFile ))
using (var newImage = new Bitmap( 350, 200 ))
using (var graphics = Graphics.FromImage( newImage ))
using (var stream = new MemoryStream())
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode =
InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage( srcImage, new Rectangle( 0, 0, 350, 200
) );
newImage.Save( stream, ImageFormat.Png );
var thumbNew = File( stream.ToArray(), "image/png" );
post.ImageThumbnail = thumbNew.FileContents;
}
return File( post.ImageThumbnail, post.ImageMimeType );
}
}
public ActionResult ChangeStatus( int id )
{
var post = unitOfWork.Post.Get( id );
if (post != null && post.IsDeclined || post != null &&
!post.IsApproved)
{
post.IsDeclined = false;
post.IsApproved = true;
}
else if (post != null && post.IsApproved || post !=null &&
!post.IsDeclined)
{
post.IsApproved = false;
post.IsDeclined = true;
}
unitOfWork.Post.Edit( post );
unitOfWork.Save();
return RedirectToAction( "Index","Home" );
}
}
}
后存储库:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using Common.App_Data;
using Common.Models;
namespace Common.Services.Repositories
{
public class PostRepository : IRepository<Post>, IDisposable
{
private BlogContext db;
public PostRepository( BlogContext context)
{
db = context;
}
public IEnumerable<Post> GetList()
{
return db.Posts.ToList();
}
public Post Get(int? id)
{
return db.Posts.Find( id );
}
public void Create( Post post ) // create object--Action : Posts/Create
{
post.PostedDateTime = DateTime.Now;
db.Posts.Add( post );
}
public void Delete( Post post ) // delete object --Action : Posts/Delete
{
if (post == null) return;
db.Posts.Remove(post);
}
public void Edit( Post post ) // edit object--Action : Posts/Edit
{
post.ModifiedDateTime = DateTime.Now;
db.Entry( post ).State = EntityState.Modified;
}
private bool disposed = false;
public virtual void Dispose( bool disposing )
{
if (!disposed)
{
if (disposing)
{
db.Dispose();
}
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize( this );
}
}
}
存储库:
using System.Collections.Generic;
namespace Common.Services.Repositories
{
public interface IRepository<T> where T : class
{
IEnumerable<T> GetList();
T Get(int? id);
void Create(T item);
void Edit(T item);
void Delete( T item );
}
}
工作单位:
using System;
using System.Data.Entity.Validation;
using System.Linq;
using Common.App_Data;
namespace Common.Services.Repositories
{
public class UnitOfWork : IDisposable
{
private BlogContext db;
private PostRepository postRepository;
public UnitOfWork(BlogContext mockdb, PostRepository mockpostRepository)
{
db = mockdb;
postRepository = mockpostRepository;
}
public PostRepository Post => postRepository ?? (postRepository = new
PostRepository( db ));
public void Save()
{
try
{
db.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
var raise =
dbEx.EntityValidationErrors.Aggregate<DbEntityValidationResult,
Exception>(dbEx, (current1, validationErrors) =>
validationErrors.ValidationErrors.Select(validationError =>
$"{validationErrors.Entry.Entity.ToString()}:
{validationError.ErrorMessage}").Aggregate(current1, (current, message) =>
new InvalidOperationException(message, current)));
throw raise;
}
}
private bool disposed = false;
public virtual void Dispose( bool disposing )
{
if (disposed) return;
if (disposing)
{
db.Dispose();
}
disposed = true;
}
public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this );
}
}
}
private bool disposed = false;
public virtual void Dispose( bool disposing )
{
if (disposed) return;
if (disposing)
{
db.Dispose();
}
disposed = true;
}
public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this );
}
}
}
好吧,其实在第一次测试的时候,我想抗议一下,执行GetList()方法的时候,返回的是posts列表(是的,一般情况下——或者结果不为零),我大概需要使用mocks在这里,但我开始对它们感到困惑,越简单越好。测试未通过((
测试尝试#1
using Common.Services.Repositories;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests.Test
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void GetListOfPostsReturnsNotNull()
{
//Arrange
var unitOfWork = new UnitOfWork();
//Act
var testList = unitOfWork.Post.GetList();
//Asset
Assert.IsNotNull(testList);
}
}
}
测试尝试#2
[TestMethod]
public void GetListOfPostsReturnsNotNull()
{
//Arrange
var mockDb = new Mock<BlogContext>();
var mockRep = new Mock<PostRepository>();
var unitOfWork = new UnitOfWork(mockDb.Object,mockRep.Object);
//Act
var testList = unitOfWork.Post.GetList();
//Asset
Assert.IsNotNull(testList);
}
解决方案
public void GetListOfPostsReturnsNotNull()
{
mockDb = new Mock<BlogContext>();
mockRep = new Mock<IRepository<Post>>();
mockUnitOfWork = new Mock<UnitOfWork>(mockDb,mockRep);
//Arrange
var posts = new List<Post>()
{
new Post
{
Id = 1,
Title = "Amazon",
Description = "Amazon daily deals for Tuesday, April 17",
Category = "Technology",
Content =
"If you\'re looking to add a little extra...",
Author = "blogger123@com.com",
},
new Post
{
Id = 2,
Title = "Ford",
Description = "Ford charges into electric bike-sharing",
Category = "Technology",
Content = "I got to try one out...",
Author = "blogger123@com.com",
}
};
mockUnitOfWork.Setup(x => x.Post.GetList()).Returns(posts); \\ ERROR
//Act
var testList = mockUnitOfWork.Object.Post.GetList();
//Asset
Assert.IsNotNull(testList);
}
}
}
推荐阅读
- powerbi - Power BI (DAX) 多筛选器总和
- c++ - CGAL - 表面网格参数化
- mongodb - 使用聚合框架返回自定义结果
- pyspark - 无法使用 PySpark 创建具有 DateType 的字段
- r - 联合后验分布的等高线图
- python - 如何使用 pytest-qt 点击 QMessageBox?
- mysql - 另一个“#1054:‘字段列表’中的未知列”之谜
- ubuntu - 如何在 ubuntu v18.04 中将 systemd 升级到特定版本?
- material-ui - 颜色随时出现在ui中
- sql - 使用 Sequelize 选择所有有效的起始字母