I am using the Moq framework in my Unit Test.
This is the UpdateApplication test method:
[TestMethod]
public void UpdateApplication()
{
const string newAplpicationName = "NewApplication1";
var data =
new[]
{
new Application { Id = 1, Name = "Application1" }, new Application { Id = 2, Name = "Application2" },
new Application { Id = 3, Name = "Application3" }, new Application { Id = 4, Name = "Application4" }
}
.AsQueryable();
var mockSet = new Mock<DbSet<Application>>();
mockSet.As<IQueryable<Application>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Application>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Application>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Application>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
mockSet.Setup(m => m.AddOrUpdate(It.IsAny<Application[]>())).Callback(
(Application[] apps) =>
{
apps.FirstOrDefault(m => m.Id == 1).Name = newAplpicationName;
}).Verifiable(); // <-- Exception
var mockContext = new Mock<AppDbContext>();
mockContext.Setup(c => c.Applications).Returns(mockSet.Object);
// Act
var commandHandler = new UpdateApplicationCommandHandler(mockContext.Object);
var commandArg = new ApplicationCommandArg { Id = 1, Name = newAplpicationName };
commandHandler.Execute(new UpdateApplicationCommand(commandArg));
// Verify
mockContext.Verify(m => m.SaveChanges(), Times.Once());
}
I got an exception when ran the test:
An exception of type 'System.NotSupportedException' occurred in Moq.dll but was
not handled in user code
Additional information: Expression references a method that does not belong to
the mocked object: m => m.AddOrUpdate(It.IsAny())
at Moq.Mock.ThrowIfNotMember(Expression setup, MethodInfo method)
at Moq.Mock.c__DisplayClass19`1.b__18()
at Moq.PexProtector.Invoke[T](Func`1 function)
at Moq.Mock.Setup[T](Mock`1 mock, Expression`1 expression, Condition condition)
at Moq.Mock`1.Setup(Expression`1 expression)
at UpdateApplication() in UpdateApplicationCommandTests.cs:line 39
How should I write unit tests for update and delete actions using Moq?
This variant of UpdateApplication unit test method is working for me but I am not sure if it is correct:
[TestMethod]
public void UpdateApplication()
{
const string newAplpicationName = "NewApplication1";
var data =
new[]
{
new Application { Id = 1, Name = "Application1" }, new Application { Id = 2, Name = "Application2" },
new Application { Id = 3, Name = "Application3" }, new Application { Id = 4, Name = "Application4" }
}
.AsQueryable();
var mockSet = new Mock<DbSet<Application>>();
mockSet.As<IQueryable<Application>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Application>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Application>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Application>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var mockContext = new Mock<AppDbContext>();
mockContext.Setup(m => m.Applications).Returns(mockSet.Object);
// Act
var commandHandler = new UpdateApplicationCommandHandler(mockContext.Object);
var commandArg = new ApplicationCommandArg { Id = 1, Name = newAplpicationName };
commandHandler.Execute(new UpdateApplicationCommand(commandArg));
Assert.AreEqual(newAplpicationName, data.First(m => m.Id == 1).Name);
mockContext.Verify(m => m.SaveChanges(), Times.Once());
}
But I still have a problem with my DeleteApplicationCommandTest.
When I run the test I get an excepton "Expected invocation on the mock exactly 3 times, but was 2 times: m => m.Applications".
This is test method:
[TestMethod]
public void DeleteApplication()
{
var data =
new[]
{
new Application { Id = 1, Name = "Application1" }, new Application { Id = 2, Name = "Application2" },
new Application { Id = 3, Name = "Application3" }, new Application { Id = 4, Name = "Application4" }
}
.AsQueryable();
var mockSet = new Mock<DbSet<Application>>();
mockSet.As<IQueryable<Application>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Application>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Application>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Application>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var mockContext = new Mock<AppDbContext>();
mockContext.Setup(m => m.Applications).Returns(mockSet.Object);
// Act
var commandHandler = new DeleteApplicationCommandHandler(mockContext.Object);
var commandArg = new ApplicationCommandArg { Id = 1 };
commandHandler.Execute(new DeleteApplicationCommand(commandArg));
// Verify
mockSet.Verify(m => m.Remove(It.IsAny<Application>()), Times.Once());
mockContext.Verify(m => m.SaveChanges(), Times.Once());
mockContext.VerifyGet(m => m.Applications, Times.Exactly(3));
}
This is my Execute method of DeleteApplicationCommandHandler:
public override void Execute(DeleteApplicationCommand command)
{
Debug.WriteLine("DeleteApplicationCommand executed");
var application = this.DbContext.Applications.FirstOrDefault(m => m.Id == command.CommandArg.Id);
if (application == null)
{
throw new Exception(string.Format("Application with id {0} was not found", command.CommandArg.Id));
}
this.DbContext.Applications.Remove(application);
this.DbContext.SaveChanges();
}
Why DeleteApplication test method fails?
The problem is that AddOrUpdate is an extension method. Moq cannot mock extension methods, so you'll need to find another way to achieve your test coverage.
Related
I have two collections - accounts and users. Logically, they have a many-to-many relationship. In mongo, they look like this:
users
[
{
"userId": "3Nv6yHTC6Eiq0SaMyBcDlA",
"emailAddress": "foo#bar.com",
"userAccounts":
[
{
"accountId": "tgvANZWSZkWl0bAOM00IBw"
}
]
}
]
accounts
[
{
"accountId": "tgvANZWSZkWl0bAOM00IBw",
"accountCode": "foo",
"userIds":
[
"3Nv6yHTC6Eiq0SaMyBcDlA"
]
}
]
Can I use a single Linq operation using the mongo Linq driver to join the account collection to the user's userAccounts child documents, such that I return a user (or list of users) with the accountCode included within each userAccount (the ExtendedUserAccount within the ExtendedUser in the example below)? Or do I need to forget Linq and use the Aggregate class instead?
The query below results in an ExpressionNotSupportedExpression from the mongo Linq driver. If I split the query to get the user first and then join to the accounts collection, it works.
Here is some code!
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
var settings = MongoClientSettings.FromConnectionString("yourconnectionstring");
settings.LinqProvider = MongoDB.Driver.Linq.LinqProvider.V3;
var client = new MongoClient(settings);
var conventionPack = new ConventionPack { new CamelCaseElementNameConvention(), new IgnoreExtraElementsConvention(true) };
ConventionRegistry.Register("camelCase", conventionPack, t => true);
var db = client.GetDatabase("Test");
var accountCollection = db.GetCollection<Account>("accounts");
var userCollection = db.GetCollection<User>("users");
var queryableAccounts = accountCollection.AsQueryable();
var extendedUser = userCollection.AsQueryable()
.Where(u => u.EmailAddress == "foo#bar.com")
.Select(u => new ExtendedUser(
u.UserId,
u.EmailAddress,
u.UserAccounts.Join(
queryableAccounts,
ua => ua.AccountId,
a => a.AccountId,
(ua, a) => new ExtendedUserAccount(a.AccountCode, ua.AccountId)))
)
.FirstOrDefault();
Console.WriteLine(extendedUser);
public record class User(string UserId, string EmailAddress, IEnumerable<UserAccount> UserAccounts);
public record class UserAccount(string AccountId);
public record class Account(string AccountId, string AccountCode, IEnumerable<string> UserIds);
public record class ExtendedUser(string UserId, string EmailAddress, IEnumerable<ExtendedUserAccount> UserAccounts);
public record class ExtendedUserAccount(string AccountId, string AccountCode);
I tested even with LinqProviderV3 setting on MongoClientSettings, but sadly to say that the MongoDB .Net Driver seems doesn't support it (sub-query join).
You should look for .Aggregate().
MongoDB query
db.users.aggregate([
{
$match: {
emailAddress: "foo#bar.com"
}
},
{
$lookup: {
from: "accounts",
localField: "userAccounts.accountId",
foreignField: "accountId",
pipeline: [
{
$project: {
_id: 0,
accountId: 1,
accountCode: 1
}
}
],
as: "userAccounts"
}
}
])
Approach 1: Pass the query as BsonDocument
Either you can translate the above query (the array part) to BsonDocument[] with the help of MongoDB Compass. It should be as below:
var pipeline = new BsonDocument[]
{
new BsonDocument("$match",
new BsonDocument("emailAddress", "foo#bar.com")),
new BsonDocument("$lookup",
new BsonDocument
{
{ "from", "accounts" },
{ "localField", "userAccounts.accountId" },
{ "foreignField", "accountId" },
{ "pipeline",
new BsonArray
{
new BsonDocument("$project",
new BsonDocument
{
{ "_id", 0 },
{ "accountId", 1 },
{ "accountCode", 1 }
})
} },
{ "as", "userAccounts" }
})
};
var extendedUser = userCollection.Aggregate<ExtendedUser>(pipeline)
.FirstOrDefault();
Approach 2: Working with Aggregate Fluent
var lookupPipeline = new EmptyPipelineDefinition<Account>()
.Match(new BsonDocument("$expr",
new BsonDocument("$in", new BsonArray { "$accountId", "$$accountId" })
))
.Project<Account, Account, ExtendedUserAccount>(
Builders<Account>.Projection
.Include(x => x.AccountId)
.Include(x => x.AccountCode));
var extendedUser = userCollection.Aggregate()
.Match(u => u.EmailAddress == "foo#bar.com")
.Lookup(accountCollection,
new BsonDocument { { "accountId", "$userAccounts.accountId" } },
lookupPipeline,
new ExpressionFieldDefinition<ExtendedUser, IEnumerable<ExtendedUserAccount>>(x => x.UserAccounts)
)
.FirstOrDefault();
Similar usage for MongoDB.Driver.Tests AggregateFluentTest
Demo
I am using the Moq framework for my unit test.
This is my TestMethod:
[TestFixture]
public class UpdateContactTests
{
private static readonly object[] TestValidContact =
{
new object[]
{
"Prueba", "nearlinx#gmail.com", "86456245",
new LookUpItem()
{
LookUpItemId = Guid.NewGuid(), Category = "ContactType", CategoryId = 1, Value = "Bulling Contact"
},
new Company()
{
Id = Guid.NewGuid(), Name = "Company", Email = "company#gmail.com", WebSite = "company.com",
Nda = false, Msa = false, Phone = "84876817"
}
}
};
[TestCaseSource(nameof(TestValidContact))]
[Test]
public void UpdateContact_ValidValues_UpdateContact(string name, string email, string phone, LookUpItem lookUpItem, Company company)
{
//arrange
var id = Guid.NewGuid();
var data =
new[]
{
new Contact { Id = Guid.NewGuid(), Name = "Test1", Email = "nearlinx#gmail.com", Phone = "86456245",
ContactType =
new LookUpItem()
{
LookUpItemId = Guid.NewGuid(), Category = "ContactType", CategoryId = 1, Value = "Bulling Contact"
},
Company =
new Company()
{
Id = Guid.NewGuid(), Name = "Company", Email = "company#gmail.com", WebSite = "company.com",
Nda = false, Msa = false, Phone = "84876817"
}},
new Contact { Id = id, Name = "Test2", Email = "nearlinx#gmail.com", Phone = "86456245",
ContactType =
new LookUpItem()
{
LookUpItemId = Guid.NewGuid(), Category = "ContactType", CategoryId = 1, Value = "Bulling Contact"
},
Company =
new Company()
{
Id = Guid.NewGuid(), Name = "Company", Email = "company#gmail.com", WebSite = "company.com",
Nda = false, Msa = false, Phone = "84876817"
}},
};
var contact = new Contact()
{
Id = id,
Name = name,
Email = email,
Phone = phone,
ContactType = lookUpItem,
Company = company
};
var _unitOfWork = new Mock<IUnitOfWork>();
_unitOfWork.Setup(mock => mock.Contact.Get(null, null, null)).Returns(data);
_unitOfWork.Setup(mock => mock.Company.Get(null, null, null)).Returns(new List<Company>());
_unitOfWork.Setup(mock => mock.LookUpItem.Get(null, null, null)).Returns(new List<LookUpItem>());
var contactService = new ContactService(_unitOfWork.Object);
//act
contactService.UpdateContact(contact);
//assert
Assert.That(data.First(m => m.Id == id).Name, Is.EqualTo(contact.Name));
_unitOfWork.Verify(mock => mock.Contact.Update(It.IsAny<Contact>()), Times.Once);
}
}
My problem is that when I run the test a NullReferenceException is thrown, I suppose it is because the list that has the object that I want to modify is not being assigned
I've never really used Moq and I don't know much about unit tests either, so what am I really doing wrong
Edit:
i change one of the setup because the one called when the update is done was another
instead of
_unitOfWork.Setup(mock => mock.Contact.Get(null, null, null)).Returns(data);
is
_unitOfWork.Setup(mock => mock.Contact.Where(c => c.Id == contact.Id,null,null).FirstOrDefault()).Returns(() => data.Where(c => c.Id == contact.Id));
Without seeing the rest
_unitOfWork.Setup(mock => mock.Contact.Get(null, null, null))
the mock.Contact returns null
best thing is to debug the test, breakpoint the first line and step through to find exactly where it is throwing the null exception
I found the solution
I was doing the set like this
_unitOfWork.Setup(mock => mock.Contact.Where(c => c.Id == contact.Id,null,null).FirstOrDefault()).Returns(() => data.Where(c => c.Id == contact.Id));
while in the update method the condition was this
_unitOfWork.Setup(mock => mock.Contact.Where(c => c.Id == contact.Id).FirstOrDefault()).Returns(() => data.Where(c => c.Id == contact.Id));
changing the condition in the method fixed the problem
I want to select only id's from List of objects in another List:
https://dotnetfiddle.net/Leu1AD
I need to use only linq Select or SelectMany:
var obj = offices.Select(p => new {Id = p.Id, Employess = p.Employess}).ToList();
Currently I get following result:
[
{
"Id":1,
"Employess":[
{
"Id":1,
"FirstName":"a",
"LastName":"b"
},
{
"Id":2,
"FirstName":"c",
"LastName":"d"
}
]
},
{
"Id":2,
"Employess":[
{
"Id":3,
"FirstName":"e",
"LastName":"f"
},
{
"Id":4,
"FirstName":"g",
"LastName":"h"
}
]
}
]
But I need this result:
[
{
"Id":1,
"Employess":[
{
"Id":1
},
{
"Id":2
}
]
},
{
"Id":2,
"Employess":[
{
"Id":3
},
{
"Id":4
}
]
}
]
Do you have any ideas how to do that?
One method to get the result to be the format you want is along the lines of
var obj = offices.Select(p => new {Id = p.Id, Employess = p.Employess.Select(y=> new {y.Id})}).ToList();
ends up as
[{"Id":1,"Employess":[{"Id":1},{"Id":2}]},{"Id":2,"Employess":[{"Id":3},{"Id":4}]}]
You need a second Select that only selects the Employee Id.
var obj = offices.Select(o => new {Id = o.Id, Employess = o.Employess.Select(e => new { Id = e.Id })});
alter your line
var obj = offices.Select(p => new {Id = p.Id, Employess = p.Employess}).ToList();
to
var obj = offices.Select(p => new {Id = p.Id, Employess = p.Employess.Select(x=>new{Id=x.Id})}).ToList();
To have the expected result just replace:
Employess = p.Employess
With
Employess = p.Employess.Select(e => new { e.Id })
Finally you have this LINQ statement:
var obj = offices.Select(p => new {Id = p.Id, Employess = p.Employess.Select(e => new { e.Id })}).ToList();
I am creating a dashboard comprised of several Telerik UI for ASP.NET MVC Charts. The first graph is supposed to plot sales volume month by month for a given agent. My controller class is as follows:
static List<SalesAgentViewModel> SalesAgentData = new List<SalesAgentViewModel>(){
new SalesAgentViewModel
{
SalesAgentID = 1,
ChartData = new List<SalesAgentChartItem>()
{
new SalesAgentChartItem
{
MonthName="January",
MerchantMCVolume=25000,
MerchantVisaVolume=500000
},
new SalesAgentChartItem
{
MonthName="February",
MerchantMCVolume = 225000,
MerchantVisaVolume = 725000
},
new SalesAgentChartItem
{
MonthName="March",
MerchantMCVolume = 75000,
MerchantVisaVolume = 150250
}
}
},
new SalesAgentViewModel
{
SalesAgentID = 2,
ChartData = new List<SalesAgentChartItem>()
{
new SalesAgentChartItem
{
MonthName="January",
MerchantMCVolume = 98000,
MerchantVisaVolume = 75250
},
new SalesAgentChartItem
{
MonthName = "February",
MerchantMCVolume = 87500,
MerchantVisaVolume = 68250
},
new SalesAgentChartItem
{
MonthName = "March",
MerchantMCVolume = 68500,
MerchantVisaVolume = 78125
}
}
}
};
[Authorize(Roles ="Admin,SalesAgent")]
public ActionResult ReadSalesAgentData([DataSourceRequest]DataSourceRequest request)
{
return Json(SalesAgentData.ToDataSourceResult(request));
}
[Authorize(Roles ="Admin,SalesAgent")]
public ActionResult ReadGraphData(int id)
{
return Json(SalesAgentData.First(v => v.SalesAgentID == id).ChartData);
}
I then new up a chart in my view as follows:
<div class="container" id="dashboard">
#(Html.Kendo().Chart<SalesAgentChartItem>()
.Name("SalesAgentVolume")
.Title("Sales Agent Volume")
.DataSource(dataSource => dataSource
.Read(read => read.Action("ReadGraphData","SalesAgents"))
)
.Series(series=>
{
series.Line(s => s.MerchantMCVolume).CategoryField("Mastercard Volume");
series.Line(s => s.MerchantVisaVolume).CategoryField("Visa Volume");
})
.CategoryAxis(axis=>axis
.Categories(model=>model.MonthName))
)
</div>
When I run the application, the graph is rendered but an HTTP 500 is thrown stating:
System.ArgumentException: The parameters dictionary contains a null entry
for parameter 'id' of non-nullable type 'System.Int32' for method
'System.Web.Mvc.ActionResult ReadGraphData(Int32)' in
'Web.Controllers.SalesAgentsController'. An optional parameter must be a
reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
My intent is to show the volume for a given agent identified by SalesAgentID. How would I accomplish this?
I have made a C# LINQ query that categorizes (or groups) by a property and I am almost sure there is a better way. My project is full of these queries so I am really interested on how to achieve this the proper way.
This is how my query look like:
var g = _repository.GetEmployees();
var result =
g.GroupBy(x => x.City, (key, group) => group.First())
.Select(x => new {
city = x.City,
employees = g
.Where(y=>y.EmployeeID == x.EmployeeID)
.Select(y=> new {
fullname = y.FirstName + " " + y.LastName,
title = y.Title
})
.OrderBy(y=>y.fullname)
})
.OrderBy(x => x.city);
Sample of JSON output:
[
{
"city":"Barcelona",
"employees":[
{
"fullname":"Foo Bar",
"title":"Help Desk Technician"
},
{
"fullname":"Lorem Ipsum",
"title":"Information Technology Director"
}
]
},
{
"city":"London",
"employees":[
{
"fullname":"Le Query",
"title":"Information Technology Manager"
},
{
"fullname":"Please Help",
"title":"Management Information Systems Director"
}
]
}
]
The result is fine. What is the best way to achieve it?
It sounds like you just want:
var result = g.GroupBy(x => x.City, (key, group) => new {
city = key,
employees = group.Select(emp => new {
fullname = emp.FirstName + " " + emp.LastName,
title = emp.Title
})
});
In other words, you're just providing a projection for each group, which is "an anonymous type with the city, and all the employees for that city".