I am really new to XUnit and I will appreciate some help.
Controller
I have a really simple API method that adds a bug comment
[HttpPost("{bugId}/comment")]
public async Task<IActionResult> AddComment(Guid bugId, [FromBody] AddCommentForm form)
{
return await _bugService.AddComment(bugId, form) switch
{
Option<BugViewModel>.Some(var bug) => Ok(bug),
Option<BugViewModel>.None => NotFound(),
_ => throw new InvalidOperationException()
};
}
Service
The BugService method AddComment looks like this
public async Task<Option<BugViewModel>> AddComment(Guid bugId, AddCommentForm form)
{
if (await DbBugs.SingleOrDefaultAsync(x => x.Id == bugId) is { } bug)
{
var bugComment = new BugComment
{
Text = form.Text,
Commenter = form.Commenter,
CommentedAt = DateTime.Now,
Bug = bug
};
await _dbContext.BugComments.AddAsync(bugComment);
await _dbContext.SaveChangesAsync();
return new Option<BugViewModel>.Some(BugViewModel.FromData(bug));
}
return new Option<BugViewModel>.None();
}
ViewModels
I am adding the implementation of BugViewModel and BugCommentViewModel with their static FromData methods
public class BugViewModel
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public List<BugCommentViewModel> Comments { get; set; }
public static BugViewModel FromData(Bug bug)
{
return new BugViewModel
{
Id = bug.Id,
Title = bug.Title,
Description = bug.Description,
Comments = bug.Comments.Select(BugCommentViewModel.FromData).ToList()
};
}
}
public class BugCommentViewModel
{
public Guid Id { get; set; }
public string Text { get; set; }
public string Commenter { get; set; }
public DateTime CommentedAt { get; set; }
public static BugCommentViewModel FromData(BugComment comment)
{
return new BugCommentViewModel
{
Id = comment.Id,
Text = comment.Text,
Commenter = comment.Commenter,
CommentedAt = comment.CommentedAt.ToLocalTime()
};
}
}
Testing
The Unit Tests look like this
public class BugContollerTests
{
private class Fixture
{
public IBugService BugService { get; } = Substitute.For<IBugService>();
public BugController GetSut() => new BugController(BugService);
}
private readonly Fixture _fixture = new Fixture();
[Fact]
public async Task Create_ResponseBodyIsCreatedBug()
{
var bug = BugViewModel.FromData(FakeBug.I.Generate());
_fixture.BugService.Create(Arg.Any<CreateBugForm>()).Returns(bug);
var sut = _fixture.GetSut();
var response = Assert.IsAssignableFrom<CreatedAtActionResult>(await sut.Create(new CreateBugForm()));
Assert.Same(bug, response.Value);
}
[Fact]
public async Task AddComment_CommentIsAddedtoViewModel()
{
_fixture.BugService.AddComment(Arg.Any<Guid>(), Arg.Any<AddCommentForm>()).Returns(new Option<BugViewModel>.Some(new BugViewModel()));
var sut = _fixture.GetSut();
var response = Assert.IsAssignableFrom<ObjectResult>(await sut.AddComment(Guid.Empty,new AddCommentForm()));
Assert.Single(((BugViewModel) response.Value).Comments);
}
}
So, the Test method I had as an example was Create_ResponseBodyIsCreatedBug but the question I have is regarding the second test method that I have created AddComment_CommentIsAddedtoViewModel().
What I am trying to do is:
you see that the BugService.AddComment -> it adds the comment with the Bug Guid we have provided and basically we return BugViewModel with that Comment added to the Comments collection.
test: that WHEN I add a BugComment it is returned in the ViewModel response and we have added exactly one Comment to the bug.
this line
var response = Assert.IsAssignableFrom<ObjectResult>(await sut.AddComment(Guid.Empty,new AddCommentForm()));
the response.Value is a BugViewModel with empty properties and the Comment property is empty without my inserted comment.
And on this line I get an exception as
Assert.Single(((BugViewModel) response.Value).Comments);
as the Comments property is null.
Look, at this line
_fixture.BugService.AddComment(Arg.Any<Guid>(), Arg.Any<AddCommentForm>())
.Returns(
new Option<BugViewModel>.Some(new BugViewModel()));
You setup your fake BugService to return an empty BugViewModel, which indeed would have Comments equal to null because you initialize it nowhere except FromData.
Use same approach you did in a first test
_fixture.BugService.AddComment(Arg.Any<Guid>(), Arg.Any<AddCommentForm>())
.Returns(
new Option<BugViewModel>.Some(
BugViewModel.FromData(FakeBug.I.Generate())));
Or introduce a constructor
public BugViewModel(Guid id, string title, string description, List<BugCommentViewModel> comments)
{
Id = id;
Title = title;
Description = description;
Comments = comments;
}
// Comments are not NULL now
public BugViewModel(): this(Guid.Empty, string.Empty, string.Empty, new List<BugCommentViewModel>())
{
}
In addition, I don't see any purpose of unit testing a mocked dependency of BugController .
Assert.Single(((BugViewModel) response.Value).Comments);
Better move this logic to a separate BugServiceTest which would validate the number of comments.
One more suggestion in regards to unit testing is instead of relying on ObjectResult, write better three tests:
[Fact]
public async Task AddComment_CommentIsAddedtoViewModel_Success()
{
_fixture.BugService.AddComment(Arg.Any<Guid>(), Arg.Any<AddCommentForm>())
.Returns(
new Option<BugViewModel>.Some(
BugViewModel.FromData(FakeBug.I.Generate())));
var sut = _fixture.GetSut();
Assert.IsAssignableFrom<OkResult>(
await sut.AddComment(Guid.Empty,new AddCommentForm()));
}
[Fact]
public async Task AddComment_BugNotFound()
{
_fixture.BugService.AddComment(Arg.Any<Guid>(), Arg.Any<AddCommentForm>())
.Returns(Option<BugViewModel>.None);
var sut = _fixture.GetSut();
Assert.IsAssignableFrom<NotFoundResult>(
await sut.AddComment(Guid.Empty,new AddCommentForm()));
}
public async Task AddComment_ThrowsForNonsenseData()
{
// Idk Option is value or reference type, so I use default(T)
_fixture.BugService.AddComment(Arg.Any<Guid>(), Arg.Any<AddCommentForm>())
.Returns(default(Option<BugViewModel>));
var sut = _fixture.GetSut();
await Assert.ThrowsAsync<InvalidOperationException>(
() => sut.AddComment(Guid.Empty, new AddCommentForm()));
}
Tell me if that helps
The problem is that you are mocking BugService.
You set mock to AddComment method and setup to return HARD CODED value.
That's why this your code is not called any more.
if (await DbBugs.SingleOrDefaultAsync(x => x.Id == bugId) is { } bug)
{
var bugComment = new BugComment
{
Text = form.Text,
Commenter = form.Commenter,
CommentedAt = DateTime.Now,
Bug = bug
};
await _dbContext.BugComments.AddAsync(bugComment);
await _dbContext.SaveChangesAsync();
return new Option<BugViewModel>.Some(BugViewModel.FromData(bug));
}
Your tests is written to check ONLY Controller.
if you want to check BugService too, you should NOT setup mocks for BugService and setup mocks to this:
DbBugs.SingleOrDefaultAsync(x => x.Id == bugId)
//and this
_dbContext.BugComments.AddAsync(bugComment);
//and this
_dbContext.SaveChangesAsync();
Related
I've seen a few questions like this floating around but I'm looking for a good explination of how to get around this. I understand that Moq can't mock the extension call, but I'm just looking for a really good example. In the current code I there is a call like
var thing = listOfthings.myList.SingleOrDefault(lt => lt.Name== "NameToFind");
I've tried
MockedlistOfThings.Setup(x => x.myList.SingleOrDefault(o => o.Name == "NameToFind")).Returns(fakeObject);
Just looking for a good work around. thanks.
To further elaborate on how this situation came up, we are currently running a translation engine against large sets of data, that has to be run line by line. This translation engine passes in an Interface called IListOfthings. listOfthings is actually holding reference data in a dictionary that is preloaded in another call higher up in the program. I have created a "fakeObject" <- dictionary that holds my fake data that the method can use. I have Mocked the IlistOfthings which is passed in to the calling method. but I don't see how to fake the SingleOrDefault call.
Simplifed method below.
Public class ClassIMTesting
{
public void Translate(myObject obj, IlistOfThings listOfthings){
var thing = listOfthings.MyList.SingleOrDefault(lt => lt.Name== "NameToFind");
//Other logic here .....
}
}
public class Thing()
{
public string Name { get; set; }
public Dictionary MyDict { get; set; }
}
[TestFixture()]
public class MyCodeTest
{
MyObject myObj;
Mock<IListOfthings> listOfThings;
Thing thing;
[SetUp]
public void Setup()
{
myObj = new MyObject();
_thing = new thing();
_thing.Name = "MyName";
var myDict = new Dictionary<string, string>();
myDict.Add("70,~", "");
myDict.Add("70,145", "expectedResult");
myDict.Add("911,", "expectedResult");
thing.MyDict = myDict;
listOfThings = new Mock<IListOfthings>();
listOfThings.Setup(x => x.MyList.SingleOrDefault(o => o.Name == "MyName")).Returns(thing);
}
[TestCase("70", "~", "70070")]
[TestCase("70", "145", "expectedResult")]
[TestCase("911", "", "expectedResult")]
public void TranslateTest(string iTC, string ITCode, string expectedResult)
{
myObject.ITC = iTC;
myObject.ITCode = iTCode;
ClassIMTesting p = new ClassIMTesting();
p.Translate(myObject, listofThings.Object);
Assert.That(myObject.ITC3Code, Is.EqualTo(expectedResult));
}
}
public interface IListOfThings
{
List<Thing> MyList{ get; set; }
}
Given
public interface IListOfThings {
List<Thing> MyList { get; set; }
}
public class Thing() {
public string Name { get; set; }
public Dictionary MyDict { get; set; }
}
In order to provide a mock to satisfy the following example
public class ClassImTesting {
public Thing Translate(IlistOfThings listOfthings){
var thing = listOfthings.MyList.SingleOrDefault(lt => lt.Name== "NameToFind");
return thing
}
}
The mock just needs to return a collection that will allow the SingleOrDefault extension to behave as expected when invoked.
For example
//Arrrange
Mock<IListOfthings> listOfThings = new Mock<IListOfthings>();
var thing = new Thing {
Name = "NameToFind",
//...
};
List<Thing> list = new List<Thing>() { thing };
listOfThings.Setup(_ => _.MyList).Returns(list);
var subject = new ClassImTesting();
//Act
var actual = subject.Translate(listOfThings.Object);
//Assert
Assert.That(actual, Is.EqualTo(thing));
By having the mock return an actual List<Thing>, when
var thing = listOfthings.MyList.SingleOrDefault(lt => lt.Name== "NameToFind");
is invoked, the SingleOrDefault extension acts on a list where I can behave as expected.
I have a domain class, which looks like this:
public class Employee
{
public Guid EmployeeId { get; private set; }
public string Name { get; private set; }
public string Surname { get; private set; }
...
// other properties
public ICollection<Language> Languages { get; private set; }
= new List<Language>();
public ICollection<Skill> Skills { get; private set; }
= new List<Skill>();
public void AddLanguage(Language language)
{
if (language == null)
return;
Languages.Add(language);
}
public void DeleteLanguage(Guid languageId)
{
var languageToDelete = Languages
.SingleOrDefault(x => x.LanguageId == languageId);
if(languageToDelete == null)
throw new ArgumentException("Language entry doesn't exist.");
Languages.Remove(languageToDelete);
}
}
I would like to test given methods but I'm stuck.
I have:
[Fact]
public void AddLanguage_AfterCallWithValidObject_LanguagesCollectionContainsAddedObject()
{
var language = new Mock<Language>();
var employee = new Employee("Name", "Surname", ...);
employee.AddLanguage(language.Object);
Assert.Contains(employee.EmployeeLanguages, x => x.Language.Equals(language.Object));
}
[Fact]
public void DeleteLanguage_WhenLanguageWithGivenIdDoesntExist_ThrowArgumentException()
{
var languageToDelete = new Language("English");
var employee = new Mock<Employee>();
employee.Setup(x => x.Languages).Returns(new List<Languages>
{
new Language("Spanish"),
new Language("German")
});
employee.Object.DeleteLanguage(languageToDelete);
// Asserts here
}
In the first test I would like also assert that Languages.Add(skill) method was called but I have no idea how to do it.
Is it an elegant way to do it? I thought about mocking Add method but I'm not sure if it is a good idea.
In the second test I cannot simply mock Employee object as it is not an interface.
I thought about exposing Employee but I read I should not do that just for testing purpose.
How should I mock Languages property without exposing Employee as interface? Is it possible? Is it any good practice to do this kind of things?
Is my general concept for test these methods is okay? (I'm new in unit testing)
You should tests objects as "black box" without relying on implementation details. In your case implementation details is that Employee class uses ICollection.Add method.
And you definitely don't need to mock at all in your case.
Mock only dependencies which will make tests slow or very very very very complex to configure for the test.
[Fact]
public void AddLanguage_ShouldSaveGivenLanguage()
{
var language = new Language();
var employee = new Employee("Name", "Surname");
employee.AddLanguage(language);
var expectedLanguages = new[] { language };
employee.EmployeeLanguages.Should().BeEquivalentTo(expectedLanguages);
}
Use public API Employee class provide to setup it for the test (back box).
For testing DeleteLanguage add dummy languages through public API of the class.
[Fact]
public void DeleteLanguage_WhenLanguageExists_Remove()
{
var language1 = new Language("German");
var language2 = new Language("French");
var languageToDelete = new Language("English");
var employee = new Employee("Name", "Surname");
employee.AddLanguage(language1);
employee.AddLanguage(language2);
employee.AddLanguage(languageToDelete);
employee.DeleteLanguage(languageToDelete);
var expectedLanguages = new[] { language1, language2 };
employee.EmployeeLanguages.Should().BeEquivalentTo(expectedLanguages);
}
[Fact]
public void DeleteLanguage_WhenLanguageNotExists_ThrowException()
{
var language1 = new Language("German");
var language2 = new Language("French");
var notExistedLanguage = new Language("English");
var employee = new Employee("Name", "Surname");
employee.AddLanguage(language1);
employee.AddLanguage(language2);
Action delete = () => employee.DeleteLanguage(languageToDelete);
delete.Should()
.Throw<ArgumentException>()
.WithMessage("Language entry doesn't exist.");
}
Did you notice how cumbersome employee.EmployeeLanguages reads, you can rename property to just employee.Languages.
For readable assertions I used FluentAssertions library
For domain objects with very simple behavior and no dependencies, mocking isn't strictly needed. You can test Add simply with this:
//Arrange
var e = new Employee();
var l = new Mock<Language>();
//Act
e.AddLanguage(l.Object);
//Assert
Assert.IsTrue(e.Languages.Contains(l.Object));
Testing in this fashion you can achieve perfectly good code coverage and plenty of confidence that the Employee class works as designed.
Using c#, Moq, MSTest, LinqToExcel
I'm having trouble figuring out the best way to Setup() a mock for a response from a Linq query on a Linq-to-Excel IExcelQueryFactory.
I think I should expect the Linq Query to return something of type delegate, but I'm not quite sure what that should look like
Can anyone suggest what my Return() should look like in the Moq Setup() below?
Also, any toughts on my approach to testing and mocking these Lynq methods? Should I be approaching this differently?
Thanks! I'm going to go book up on delegates now. :)
The Test
[TestClass]
public class ThingsSheetTests
{
[TestMethod]
public void GetRows_ReturnsListOfThings()
{
// Arrange
var mockExcelQueryFactory = new Mock<IExcelQueryFactory>();
var thingsSheet = new ThingsSheet(mockExcelQueryFactory.Object, "file", "worksheet");
mockExcelQueryFactory
.Setup(x => x.Worksheet<Thing>(It.IsAny<string>))
// I think this is correctly casting to a delegate
// however ExelQuerable constructor needs arguments:
// public ExcelQueryable(IQueryProvider provider, Expression expression);
// looking into what kind of IQueryProvider and Expression I should supply.
.Returns(Action(() => new ExcelQueryable<Thing> { })); // getting closer!
// Act
thingsSheet.GetRows();
// Assert
mockExcelQueryFactory.Verify();
}
}
The Class and Method I'm testing
public class ThingsSheet
{
private string importFile;
private string worksheetName;
private IExcelQueryFactory excelQueryFactory;
public ThingsSheet(IExcelQueryFactory excelQueryFactory, string importFile, string worksheetName)
{
this.excelQueryFactory = excelQueryFactory;
this.importFile = importFile;
this.worksheetName = worksheetName;
this.AddMappings();
}
private void AddMappings()
{
excelQueryFactory.AddMapping<Thing>(t => t.Id, "Thing ID");
}
public List<Thing> GetRows()
{
excelQueryFactory.AddMapping<Thing>(t => t.Id, "Thing ID");
var things = from thing in excelQueryFactory.Worksheet<Thing>(this.worksheetName)
select new Thing { };
return things.ToList<Thing>();
}
}
You can use e.g. a method which returns your fake data.
mockExcelQueryFactory
.Setup(x => x.Worksheet<Thing>(It.IsAny<string>()))
.Returns(ExcelQueryableOfThing());
Lets say Thing class looks like this:
public class Thing
{
public string Id { get; set; }
public string Name { get; set; }
}
Then in the method ExcelQueryableOfThing() you have to mock the CreateQuery<TElement>(Expression expression) method of IQueryProvider provider. Something like this:
private ExcelQueryable<Thing> ExcelQueryableOfThing()
{
var things = new List<Thing>
{
new Thing
{
Id = "1",
Name = "Adam"
},
new Thing
{
Id = "1",
Name = "Eva"
}
}
.AsQueryable();
Mock<IQueryProvider> queryProvider = new Mock<IQueryProvider>();
queryProvider
.Setup(p => p.CreateQuery<Thing>(It.IsAny<Expression>()))
.Returns(() => things);
Expression expressionFake = Expression.Constant(new List<Thing>().AsQueryable());
return new ExcelQueryable<Thing>(queryProvider.Object, expressionFake);
}
Then in the unit test thingsSheet.GetRows() will return your fake data (Adam and Eva :). HTH
[TestMethod]
public void GetRows_ReturnsListOfThings()
{
// Arrange
Mock<IExcelQueryFactory> mockExcelFile = new Mock<IExcelQueryFactory>();
var thingsSheet = new ThingsSheet(mockExcelFile.Object, "file", "worksheet");
mockExcelFile
.Setup(x => x.Worksheet<Thing>(It.IsAny<string>()))
.Returns(ExcelQueryableOfThing());
// Act
List<Thing> rows = thingsSheet.GetRows();
// Assert
Assert.AreEqual(2, rows.Count); // Adam and Eva
}
I am trying to mock a method that returns a IEnumerable set of data, like a list of all codes.
There is an Interface ISystemService.cs that contains this method, a service class called SystemService.cs that has the method definition.
System under test is:
public static class CacheKeys
{
public const string ALLCURRENCYCODES = "CurrencyCodes";
}
public interface ICacheManager
{
T Get<T>(string key);
void Set(string key, object data, int cacheTime);
void Clear();
}
public interface ISessionManager
{
}
public interface IApplicationSettings
{
string LoggerName { get; }
int CacheTimeout { get; }
}
public class EFDbContext : DbContext
{
public DbSet<CurrencyCode> CurrencyCodes { get; set; }
}
public class CurrencyCode
{
public string Code { get; set; }
public string Description { get; set; }
public decimal CurrencyUnit { get; set; }
public int? DecimalPlace { get; set; }
public string BaseCurrencyCode { get; set; }
}
public interface ISystemService
{
IEnumerable<CurrencyCode> GetAllCurrencyCodes();
}
//SystemService.cs
public class SystemService : ISystemService
{
private readonly EFDbContext db;
private readonly ICacheManager cacheManager;
private readonly ISessionManager sessionManager;
private readonly IApplicationSettings appSettings;
public SystemService(EFDbContext dbContext, ICacheManager cacheManager, ISessionManager sessionManager, IApplicationSettings appSettings)
{
db = dbContext;
this.cacheManager = cacheManager;
this.sessionManager = sessionManager;
this.appSettings = appSettings;
}
public IEnumerable<CurrencyCode> GetAllCurrencyCodes()
{
var allCurrencyCodes = cacheManager.Get<IEnumerable<CurrencyCode>>(CacheKeys.ALLCURRENCYCODES);
if (allCurrencyCodes == null)
{
allCurrencyCodes = db.CurrencyCodes.ToList();
cacheManager.Set(CacheKeys.ALLCURRENCYCODES, allCurrencyCodes, appSettings.CacheTimeout);
}
return allCurrencyCodes;
}
Test Method
[TestMethod]
public void testCacheMiss()
{
List<CurrencyCode> currencycodes = new List<CurrencyCode>()
{
new CurrencyCode(){Id = 1, Code = "IND", Description = "India"},
new CurrencyCode(){Id = 2, Code = "USA", Description = "UnitedStates"},
new CurrencyCodes(){Id = 3, Code = "UAE", Description = "ArabEmirates"}
};
var mockEfContext = new Mock<EFDbContext>();
var mockCacheManager = new Mock<ICacheManager>();
var mockSessionManager = new Mock<ISessionManager>();
var mockAppSettings = new Mock<IApplicationSettings>();
// Setups for relevant methods of the above here, e.g. to test a cache miss
mockEfContext.SetupGet(x => x.CurrencyCodes)
.Returns(currencycodes); // Canned currencies
mockCacheManager.Setup(x => x.Get<IEnumerable<CurrencyCode>>(It.IsAny<string>()))
.Returns<IEnumerable<CurrencyCodes>>(null); // Cache miss
// Act
var service = new SystemService(mockEfContext.Object, mockCacheManager.Object,
mockSessionManager.Object, mockAppSettings.Object);
var codes = service.GetAllCodes();
// Assert + Verify
mockCacheManager.Verify(x => x.Get<IEnumerable<CurrencyCodes>>(
It.IsAny<string>()), Times.Once, "Must always check cache first");
mockEfContext.VerifyGet(x => x.CurrencyCodes,
Times.Once, "Because of the simulated cache miss, must go to the Db");
Assert.AreEqual(currencycodes.Count, codes.Count(), "Must return the codes as-is");
Since the defined constructor does not accept one parameter, how to pass the object as parameter? Please advice
If CodeService is under test, then you want to be mocking its dependencies, not the CodeService itself.
You'll need to provide Mocks for all of the dependencies of CodeService to the constructor, i.e.:
var currencycodes = new List<SomeCodes>
{
new CurrencyCodes(){Id = 1, Code = "IND", Description = "India"},
new CurrencyCodes(){Id = 2, Code = "USA", Description = "UnitedStates"},
new CurrencyCodes(){Id = 3, Code = "UAE", Description = "ArabEmirates"}
};
var mockEfContext = new Mock<EFDbContext>();
var mockCacheManager = new Mock<ICacheManager>();
var mockSessionManager = new Mock<ISessionManager>();
var mockAppSettings = new Mock<IApplicationSettings>();
// Setups for relevant methods of the above here, e.g. to test a cache miss
mockEfContext.SetupGet(x => x.SomeCodes)
.Returns(currencycodes); // Canned currencies
mockCacheManager.Setup(x => x.Get<IEnumerable<SomeCodes>>(It.IsAny<string>()))
.Returns<IEnumerable<SomeCodes>>(null); // Cache miss
// Act
var service = new CodeService(mockEfContext.Object, mockCacheManager.Object,
mockSessionManager.Object, mockAppSettings.Object);
var codes = service.GetAllCodes();
// Assert + Verify
mockCacheManager.Verify(x => x.Get<IEnumerable<SomeCodes>>(
It.IsAny<string>()), Times.Once, "Must always check cache first");
mockEfContext.VerifyGet(x => x.SomeCodes,
Times.Once, "Because of the simulated cache miss, must go to the Db");
Assert.AreEqual(currencycodes.Count, codes.Count(), "Must return the codes as-is");
Edit If you however mean that the next layer up of your code is under test, the principal is the same:
var mockCodeService = new Mock<ICodeService>();
mockCodeService.Setup(x => x.GetAllCodes())
.Returns(currencycodes); // Now we don't care whether this is from cache or db
var higherLevelClassUsingCodeService = new SomeClass(mockCodeService.Object);
higherLevelClassUsingCodeService.DoSomething();
mockCodeService.Verify(x => x.GetAllCodes(), Times.Once); // etc
Edit 2
I've fixed a couple of typos in the code, and assuming CurrencyCodes inherits SomeCodes and that your cache key is a string, and pushed it up onto a Git Gist here with the corresponding cache miss unit test as well. (I've used NUnit, but it isn't really relevant here)
allCodes is your service.. its the mock you need to be working with. You shouldn't be creating a concrete instance of your ICodeService.. your mock exists to fill that role.
So, remove this:
var service = new CodeService(allCodes.object);
Your next line should be:
var code = allCodes.Object.GetAllCodes();
But then.. this test seems completely redundant after that.. since you appear to be testing your mock..
Also, allCodes should be called serviceMock.. as that makes more sense.
Is there a way to setup and verify a method call that use an Expression with Moq?
The first attempt is the one I would like to get it to work, while the second one is a "patch" to let the Assert part works (with the verify part still failing)
string goodUrl = "good-product-url";
[Setup]
public void SetUp()
{
productsQuery.Setup(x => x.GetByFilter(m=>m.Url== goodUrl).Returns(new Product() { Title = "Good product", ... });
}
[Test]
public void MyTest()
{
var controller = GetController();
var result = ((ViewResult)controller.Detail(goodUrl)).Model as ProductViewModel;
Assert.AreEqual("Good product", result.Title);
productsQuery.Verify(x => x.GetByFilter(t => t.Url == goodUrl), Times.Once());
}
Thet test fail at the Assert and throw a null reference exception, because the method GetByFilter is never called.
If instead I use this
[Setup]
public void SetUp()
{
productsQuery.Setup(x => x.GetByFilter(It.IsAny<Expression<Func<Product, bool>>>())).Returns(new Product() { Title = "Good product", ... });
}
The test pass the Assert part, but this time is the Verify that fail saying that it is never called.
Is there a way to setup a method call with a specific expression instead of using a generic It.IsAny<>()?
Update
I tried also the suggestion by Ufuk Hacıoğulları in the comments and created the following
Expression<Func<Product, bool>> goodUrlExpression = x => x.UrlRewrite == "GoodUrl";
[Setup]
public void SetUp()
{
productsQuery.Setup(x => x.GetByFilter(goodUrlExpression)).Returns(new Product() { Title = "Good product", ... });
}
[Test]
public void MyTest()
{
...
productsQuery.Verify(x => x.GetByFilter(goodUrlExpression), Times.Once());
}
But I get a null reference exception, as in the first attempt.
The code in my controller is as follow
public ActionResult Detail(string urlRewrite)
{
//Here, during tests, I get the null reference exception
var entity = productQueries.GetByFilter(x => x.UrlRewrite == urlRewrite);
var model = new ProductDetailViewModel() { UrlRewrite = entity.UrlRewrite, Culture = entity.Culture, Title = entity.Title };
return View(model);
}
The following code demonstrates how to test in such scenarios. The general idea is that you execute the passed in query against a "real" data. That way, you don't even need "Verify", as if the query is not right, it will not find the data.
Modify to suit your needs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Moq;
using NUnit.Framework;
namespace StackOverflowExample.Moq
{
public class Product
{
public string UrlRewrite { get; set; }
public string Title { get; set; }
}
public interface IProductQuery
{
Product GetByFilter(Expression<Func<Product, bool>> filter);
}
public class Controller
{
private readonly IProductQuery _queryProvider;
public Controller(IProductQuery queryProvider)
{
_queryProvider = queryProvider;
}
public Product GetProductByUrl(string urlRewrite)
{
return _queryProvider.GetByFilter(x => x.UrlRewrite == urlRewrite);
}
}
[TestFixture]
public class ExpressionMatching
{
[Test]
public void MatchTest()
{
//arrange
const string GOODURL = "goodurl";
var goodProduct = new Product {UrlRewrite = GOODURL};
var products = new List<Product>
{
goodProduct
};
var qp = new Mock<IProductQuery>();
qp.Setup(q => q.GetByFilter(It.IsAny<Expression<Func<Product, bool>>>()))
.Returns<Expression<Func<Product, bool>>>(q =>
{
var query = q.Compile();
return products.First(query);
});
var testController = new Controller(qp.Object);
//act
var foundProduct = testController.GetProductByUrl(GOODURL);
//assert
Assert.AreSame(foundProduct, goodProduct);
}
}
}