How to Unit Test this method?
I have a problem with unit Test this method. It seems clear to me at first, but then I have a hard time understanding how to effectively inject / mock my array in 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);
}
...
}
I want to test the notExist method. I started below and then wondered how to mock the meaning of my context for a class? Is there a way to present or mock the context? I'm using moq to mock my dummy data, but I just don't see it here. Also, it is not a controller class.
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/"));
}
}
Refresh
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/"));
}
and changed my validation class to
public IUrlShortenersRepository context;
public UrlValidation(IUrlShortenersRepository repo)
{
context = repo;
...
My test run has no problem, however, it fails. But I was unable to start the project again. Bcos. My free validation class requires a parameter, which I'm not sure where to go. I have set the validation class in the model as shown below
[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; }
}
I'm not sure where to enter the parameter.
In the meantime, when I check the link below I get a clear example and so changed my test to
[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/"));
}
I am getting an invalid installation error 'system.notsupportedexception on non-virtual error thrown in fakeContext.Setup(ctx => ctx.Urls).Returns(mockSet.Object);
. I suspect my EFDContext cannot be overrated as the user explains below. I'm not sure how to fix this. My EFDContext class is declared in context as shown below.
namespace UrlShortener.Domain.Concrete
{
public class EFDbContext:DbContext
{
public DbSet<Url> Urls { get; set; }
}
}
Then I changed the EFDContext properties to virtual here
namespace UrlShortener.Domain.Concrete
{
public class EFDbContext:DbContext
{
public virtual DbSet<Url> Urls { get; set; }
}
}
My test is now running system.TargetInvocationException With an inner exception "Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type."}
. Message The type initializer for 'Castle.Proxies.DbSet
1Proxy 'throws an exception. The error has been dropped in
fakeContext.Setup(ctx => ctx.Urls).Returns(mockSet.Object);
source to share
Typically, you inject dbcontext or any other costly / complex test dependency into the class under test using some form of dependency injection (as a public network device constructor argument, using a service locator ...).
This way, you can inject a fake unit test implementation that gets clogged with fake data and / or fake expectations when calling methods.
You can wrap a thin header interface around your EFDbContext class to allow unit testing, or you can just make some properties in the real EFDbContext virtual as described 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/"));
}
source to share