请问我如何对这种方法进行单元测试?

时间:2015-07-20 02:32:08

标签: c# unit-testing

我在尝试单元测试此方法时遇到问题。我首先看起来很清楚,但后来我很难理解如何有效地将我的数组注入/模拟到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/"));

        }

}

更新

当我将测试更改为

 [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;
...

我的测试运行没有问题,但它失败了。但我无法再次运行该项目bcos我的流利验证类需要一个参数,我不知道从哪里通过。我在下面的模型中设置我的验证类

 [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);抛出了'非虚拟'错误的'system.notsupportedexception无效设置'。我怀疑我的EFDContext不能被覆盖,如下面的用户所解释。我不知道如何解决这个问题。我的EFDContext类在上下文中声明如下。

namespace UrlShortener.Domain.Concrete
{
    public class EFDbContext:DbContext
    {
        public DbSet<Url> Urls { get; set; }
    }
}

然后我将EFDContext的属性更改为虚拟

namespace UrlShortener.Domain.Concrete
{
    public class EFDbContext:DbContext
    {
        public virtual DbSet<Url> Urls { get; set; }
    }
}

我的测试现在抛出system.TargetInvocationException  WITH内部异常"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);

1 个答案:

答案 0 :(得分:0)

通常,通过使用某种形式的依赖注入(作为构造函数参数,使用公共setter,使用服务定位器,...),将dbcontext或任何其他昂贵/难以测试的依赖项注入到测试的类中

通过这种方式,您可以为单元测试注入虚假实现,这些实现存在伪造数据和/或对方法调用的虚假期望。

您可以在EFDbContext类周围包含一个精简header interface以允许进行单元测试,或者您可以在真实的EFDbContext虚拟上创建一些属性,如here所述。

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/"));

        }