请我如何单元测试这个方法
本文关键字:方法 单元测试 | 更新日期: 2023-09-27 18:03:33
我有一个问题,试图单元测试这个方法。这对我来说似乎很清楚,但后来我很难理解如何有效地注入/模拟我的数组到bdcontext
public class UrlValidation: AbstractValidator<UrlShortenerModel>
{
Uri uriResult;
private EFDbContext context;
.....
public bool notExist(string url)
{
//Check if The Url Exist . This is to stop replication of Urls.
bool exist = context.Urls.Any(x => x.OriginalUrl == url);
return (exist == false);
}
...
}
我想测试方法notExist。我从下面开始,然后卡住了如何模拟我的上下文的值到类?有什么方法可以表示或模仿上下文吗?我使用moq来模拟我的虚拟数据,但我只是没有看到这里的方式。而且它不是一个控制器类。
public void Should_know_if_Url_Exist_or_Not()
{
ArrayList arr = new ArrayList();
arr.AddRange(new Url[]
{
new Url{ UrlId = 0, UrlCode = "TYUR", OriginalUrl="https://fluentvalidation.com", IpAddress="127.0.0.1", PostedDate = DateTime.Now},
new Url{ UrlId = 1, UrlCode = "TwUR", OriginalUrl="https://facebook.com", IpAddress="127.0.0.1", PostedDate = DateTime.Now},
new Url{ UrlId = 2, UrlCode = "TkUR", OriginalUrl="https://youtube.com/", IpAddress="127.0.0.1", PostedDate = DateTime.Now}
});
Assert.IsTrue(val.notExist("https://www.youtube.com/"));
Assert.IsFalse(val.notExist("https://www.facebook.com/"));
}
}
When i Changed my test to
[TestMethod]
public void Should_know_if_Url_Exist_or_Not()
{
Mock<IUrlsRepository> mock = new Mock<IUrlsRepository>();
mock.Setup(u => u.Urls).Returns(new Url[] {
new Url{ UrlId = 0, UrlCode = "TYUR", OriginalUrl="https://fluentvalidation.com", IpAddress="127.0.0.1", PostedDate = DateTime.Now},
new Url{ UrlId = 1, UrlCode = "TwUR", OriginalUrl="https://facebook.com", IpAddress="127.0.0.1", PostedDate = DateTime.Now},
new Url{ UrlId = 2, UrlCode = "TkUR", OriginalUrl="https://youtube.com/", IpAddress="127.0.0.1", PostedDate = DateTime.Now}
}.AsQueryable());
var validator = new UrlValidation(mock.Object);
Assert.IsTrue(validator.notExist("https://www.youtuberre.com/"));
Assert.IsFalse(validator.notExist("https://www.facebook.com/"));
}
并将验证类修改为
public IUrlShortenersRepository context;
public UrlValidation(IUrlShortenersRepository repo)
{
context = repo;
...
我的测试运行没有问题,但是,它失败了。但我不能再次运行项目,因为我流畅的验证类需要一个参数,我不确定在哪里通过。我在模型中设置了验证类,如下所示
[Validator(typeof(UrlValidation))]
public class UrlShortenerModel
{
//The Model for the Home form. The Url shortener form.
public string strUrl { get; set; }
public IEnumerable<Url> urlList { get; set; }
}
我不确定在哪里注入参数。
当我检查下面提供的链接时,我得到了一个清晰的例子,因此修改了我的测试 [TestMethod]
public void Should_know_if_Url_Exist_or_Not()
{
var arr = new List<Url>
{
new Url{ UrlId = 0, UrlCode = "TYUR", OriginalUrl="https://fluentvalidation.com", IpAddress="127.0.0.1", PostedDate = DateTime.Now},
new Url{ UrlId = 1, UrlCode = "TwUR", OriginalUrl="https://facebook.com", IpAddress="127.0.0.1", PostedDate = DateTime.Now},
new Url{ UrlId = 2, UrlCode = "TkUR", OriginalUrl="https://youtube.com/", IpAddress="127.0.0.1", PostedDate = DateTime.Now}
}.AsQueryable();
var mockSet = new Mock<DbSet<Url>>();
mockSet.As<IQueryable<Url>>().Setup(m => m.Provider).Returns(arr.Provider);
mockSet.As<IQueryable<Url>>().Setup(m => m.Expression).Returns(arr.Expression);
mockSet.As<IQueryable<Url>>().Setup(m => m.ElementType).Returns(arr.ElementType);
mockSet.As<IQueryable<Url>>().Setup(m => m.GetEnumerator()).Returns(arr.GetEnumerator());
var fakeContext = new Mock<EFDbContext>();
fakeContext.Setup(ctx => ctx.Urls).Returns(mockSet.Object);
var validator = new UrlValidation();
validator.context = fakeContext.Object;
Assert.IsTrue(validator.notExist("https://www.youtube.com/"));
Assert.IsFalse(validator.notExist("https://www.facebook.com/"));
}
我明白这个系统。在fakeContext.Setup(ctx => ctx.Urls).Returns(mockSet.Object);
上抛出的非虚拟错误上的无效设置。我怀疑我的EFDContext不能被覆盖,如下面的用户解释。我不知道如何解决这个问题。我的EFDContext类在上下文中声明如下:
namespace UrlShortener.Domain.Concrete
{
public class EFDbContext:DbContext
{
public DbSet<Url> Urls { get; set; }
}
}
然后我将EFDContext的属性更改为virtual,这里
namespace UrlShortener.Domain.Concrete
{
public class EFDbContext:DbContext
{
public virtual DbSet<Url> Urls { get; set; }
}
}
我的测试现在抛出系统。TargetInvocationException与一个内部异常"Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type."}
。消息The type initializer for 'Castle.Proxies.DbSet
1Proxy抛出异常。'在
fakeContext.Setup(ctx => ctx.Urls).Returns(mockSet.Object);
通常,您可以通过使用某种形式的依赖注入(作为构造函数参数,使用公共setter,使用服务定位器,…)将dbcontext或任何其他昂贵/难以测试的依赖注入到被测类中。
这样你就可以为单元测试注入一个假的实现,这个实现是用假的数据和/或对方法调用的假期望来存根的。
你可以在你的EFDbContext类周围包装一个瘦头接口来允许单元测试,或者你可以在真正的EFDbContext上设置一些虚的属性,就像这里解释的那样。
public void Should_know_if_Url_Exist_or_Not()
{
ArrayList arr = new ArrayList();
arr.AddRange(new Url[]
{
new Url{ UrlId = 0, UrlCode = "TYUR", OriginalUrl="https://fluentvalidation.com", IpAddress="127.0.0.1", PostedDate = DateTime.Now},
new Url{ UrlId = 1, UrlCode = "TwUR", OriginalUrl="https://facebook.com", IpAddress="127.0.0.1", PostedDate = DateTime.Now},
new Url{ UrlId = 2, UrlCode = "TkUR", OriginalUrl="https://youtube.com/", IpAddress="127.0.0.1", PostedDate = DateTime.Now}
});
var fakeContext = new Mock<EFDbContext>();
fakeContext
.Setup(ctx => ctx.Urls)
.Returns(arr);
var validator = new UrlValidation(fakeContext.Object);
Assert.IsTrue(val.notExist("https://www.youtube.com/"));
Assert.IsFalse(val.notExist("https://www.facebook.com/"));
}