I have 3 layers in Asp.Net Web API. The controller layer, service layer and repository layer implemented using EF.
I am new to unit testing and have a simple function that gets a person by their id in the database and nothing else.
Basically the service layer calls
Unit_Work.Person_Repository.GetPersonByID(id);
and the Repository does this:
return context.chapters.Where(p=>p.chapterID==id).SingleOrDefault();
What kind of Unit Test would i write on this.
should i use the database or a mock implementation.
I thought of using Sql Server Compact populating it with a mock person and then trying to get that person by ID is this correct.?
Thanks in advance to all those that answer.
If you are using entity framework you can't unit test your data access layer.
Solution provided by Erik Alsmyr is very wrong!
Look here why - What's the point of in memory IDbSet?.
When you use in memory db sets you are running Linq to Objects. When you use EF's DbContext your Linq is converted to SQL. Those are two different things!
It is very easy to write code that will work with in memory db set (all your unit tests will pass and you will be happy) just to notice runtime error first time you try to hit database.
Let's modify this code a bit. I don't think FULLNAME should have setter if we are using FIRSTNAME, LASTNAME. It should be calculated from FIRSTNAME and LASTNAME.
class User
{
public string FIRSTNAME { get; set; }
public string LASTNAME { get; set; }
public string FULLNAME
{
get { return string.Format("{0}, {1}", LASTNAME, FIRSTNAME }
}
User(string firstName, string lastName)
{
this.FIRSTNAME = firstName;
this.LASTNAME = lastName;
}
}
Now you can write test like this and it will pass (of course after you implement it in controller)
public IMyEntities GetRepoWithUsers(params User[] users)
{
var inMemoryUsers = new InMemoryDbSet<User>();
var mockData = new Mock<IMyEntities>();
mockData.Setup(m => m.Users).Returns(inMemoryUsers);
return mockData.Object;
}
[Test]
public void GetUserByFullname()
{
var ankaArne = new User("Arne", "Anka");
var bjornBertil = new User("Bertil", "Björn");
var repo = GetRepoWithUsers(ankaArne, bjornBertil);
var usersController = new UsersController(repo);
var found = usersController.GetUser("Anka, Arne");
Assert.NotNull(found);
Assert.AreEqual("Anka", found.LASTNAME);
Assert.AreEqual("Arne", found.FIRSTNAME);
}
But when you run it against 'real' DbContext and 'real' DbSet it will throw because you can't do Linq queries on calculated properties. Only on those that are mapped to database columns. So what's the point of that test?
You can use the xml/ .csv file for data. ie, you need to fetch the ID, chapter details from the xml file inside the unit test project. Then you have to pass the id as the parameter then check the return values with the data fetch from xml file. if you dont understand let me know. You cn create unit test project by add-new project options. then on vs2010 ther r options to add xml file for fetching the data to be tested.
your 3rd question is also correct. u cn populate the data from database and check the data with the return value
I recommend mocking and injecting an Entity framework context into your repository.
We do this using something similar to http://nuget.org/packages/FakeDbSet/
Then our unit tests look like this:
[TestFixture]
class UsersControllerTester
{
private Mock<IMyEntities> mockData = null;
[SetUp]
public void Setup()
{
// Create fake data
var inMemoryUsers = new InMemoryDbSet<User>();
inMemoryUsers.Add(new User { ID = 1, FIRSTNAME = "Arne", LASTNAME = "Anka", EMAIL = "arne.anka#email.com", FULLNAME = "Anka, Arne", USERNAME = "arne.anka" });
inMemoryUsers.Add(new User { ID = 2, FIRSTNAME = "Bertil", LASTNAME = "Björn", EMAIL = "bertil.bjorn#email.com", FULLNAME = "Björn, Bertil", USERNAME = "bertil.bjorn" });
inMemoryUsers.Add(new User { ID = 3, FIRSTNAME = "Carl", LASTNAME = "Cool", EMAIL = "carl.cool#email.com", FULLNAME = "Cool, Carl", USERNAME = "carl.cool" });
inMemoryUsers.Add(new User { ID = 4, FIRSTNAME = "David", LASTNAME = "Dûsk", EMAIL = "david.dusk#email.com", FULLNAME = "Dûsk, David", USERNAME = "david.dusk" });
// Create mock unit of work
mockData = new Mock<IMyEntities>();
mockData.Setup(m => m.Users).Returns(inMemoryUsers);
}
[Test]
public void GetUser()
{
// Test
var usersController = new UsersController(mockData.Object);
// Invoke
User user1 = usersController.GetUser("1");
// Assert
Assert.NotNull(user1);
Assert.AreEqual(1, user1.ID);
Assert.AreEqual("Anka", user1.LASTNAME);
}
Related
I am new to unit testing and was trying to write unit tests for controllers' action methods using Moq Library and Xunit framework. I created a list of departments as mock data and tried testing it by passing in Returns() method with the Setup() method of the Moq. But it shows error
"Cannot convert type 'System.Collections.Generic.List<DotNetMvcDemo.Models.Department>' to 'System.Web.Mvc.ActionResult' via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion".
public ActionResult Index()
{
//var departments = db.Departments.Include(d => d.CreatorUser).Include(d => d.UpdaterUser);
var departments = new List<Department>();
using (var db = new ApplicationDbContext())
{
departments = db.Departments.ToList();
}
var departmentsViewList = new List<DepartmentViewModel>();
foreach (var department in departments)
{
var departmentView = new DepartmentViewModel();
departmentView.Id = department.Id;
departmentView.Name = department.Name;
departmentView.Description = department.Description;
departmentsViewList.Add(departmentView);
}
return View(departmentsViewList);
}
public Mock<DepartmentsController> mock = new Mock<DepartmentsController>();
private readonly DepartmentsController _controller;
public DepartmentControllerTest()
{
_controller = new DepartmentsController();
}
private static List<Department> GetTestDepartments()
{
var mockDepartments = new List<Department>
{
new Department()
{
Id = 1,
Name = "test one",
Description = "test desc"
},
new Department()
{
Id = 2,
Name = "test two",
Description = "test desc"
},
new Department()
{
Id = 3,
Name = "test three",
Description = "test desc"
}
};
return mockDepartments;
}
[Fact]
public void Can_Get_Department()
{
//Arrange
mock.Setup(m => m.Index()).Returns(GetTestDepartments() as ActionResult);
//Act
//Assertion
}
I looked for solution how to write unit test with Moq and xunit in ASP.NET MVC application. But I could not relate to my problem as most of the tutorials used Interface/ Repository Pattern to do unit testing and I have only controllers to work with. Can I get a suggestion or a guideline how can I properly write unit test for CRUD operations?
First, you actually need to create interfaces and service layer for testing. If you can't write simple test, then it something is wrong in code and needs to be refactored.
Second, controller should not contain any logic, and so there is no need to unit test controllers. All this code should be in a service somewhere.
And finally, for this case, you need to setup mock of db.Departments.ToList() to return value of GetTestDepartments(), not Index() action, then you assert result of Index() to whatever you want...
In addition, you don't "unit test" CRUD operations, you integration, or smoke test them, and for that, you should use in-memory database...
So I am kind of new to Web services, and I am trying to wrap my head around how it works.
Basically, I need to receive an ID from one side, pass it to another source which returns the employee information.
Now, this is what I did so far:
[WebMethod(Description = "Returns employee object for id")]
public Employee GetEmployeeById(int Id)
{
// how to ?
return new Employee( // data from outer source);
}
I see the wsdl, where you can send the id to the function.
How does the other source get the information from my service?
The another source means that anywhere you want to connect to. Maybe it's a database, other web service, or only a text file....
For example, you have a db that saves employee data. You can use Entity Framework to get employee data from db.
Like this:
[WebMethod(Description = "Returns employee object for id")]
public Employee GetEmployeeById(int Id)
{
using(var context = new MyDbContext())
{
var emp = context.Employees.Where(x => x.Id == Id).FirstOrDefault();
return emp;
}
}
I write below code to log/store logs with an objects to Azure Table Storage by using SeriLog, But I got the object stored in "RenderedMessage" column (in azure table) or "Data" column, While I need to store each field/property in the class to a separated column in the Table storage.
Please see below:
var _simpleTransation = new SimpleTransation(99, "some title9", "type99");
var logger = new LoggerConfiguration()
.WriteTo.AzureTableStorage(storage,LogEventLevel.Information,null, "LogStorage",false,null,null,null,false)
.Enrich.WithProperty("SimpleTransation", "#_simpleTransation", true)
.Enrich.FromLogContext()
.CreateLogger()
.ForContext<SimpleTransation>();
logger.Information<SimpleTransation>("{#SimpleTransation}", _simpleTransation);
So I need to add columns to azure storage table that represent my object fields and not serialize my whole object inside RenderedMessage log?
So I need to add columns to azure storage table that represent my object fields and not serialize my whole object inside RenderedMessage log?
You could use propertyColumns attribute to add a new column when you write to azure table storage.
Note:You need to call Enrich.FromLogContext() and the property would show up in the Azure Table Storage.
Here is an article about Enrichment. Log events can be enriched with properties in various ways.
You need to use LogContext.PushProperty to add what property and value you want to add.
You could refer to the following code to have a try:
static void Main(string[] args)
{
var storage = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
string tableName = "table411";
var exampleuser = new User { Id = 1, Name = "joey", Age = 23 };
var _log = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.AzureTableStorageWithProperties(storage, propertyColumns: new string[] { "UserId" });
LogContext.PushProperty("UserId", exampleuser.Id);
var logger = _log.CreateLogger();
logger.Information("{#User}",exampleuser);
}
class User
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
Screen shot:
I have a LINQ expression:
var users = db.Relationships.Where(i => i.RelationshipId== relId)
.Select(s => s.User).Distinct().Select(s => new UserViewModel() {
Username = s.Username,
LastActiveDateTime = s.LastActive, // DateTime, I want it to be a string filtered through my custom GetFriendlyDate method
}).ToList();
// convert all DateTimes - yuck!
foreach (var userViewModel in users) {
userViewModel.LastActive = DateFriendly.GetFriendlyDate(userViewModel.LastActiveDateTime);
}
This solution is working, but it feels wrong to
have to iterate over all users after getting them from the db, just to reformat a property on every single one
have a DateTime property on my ViewModel just so that it can later be converted to a string and never touched again
Is there any way I can use the GetFriendlyDate method directly within the query?
Possible solutions, worth to mention:
Have a getter property of your ViewModel, which would return transformed string, something like:
public string LastActive
{
get
{
return DateFriendly.GetFriendlyDate(LastActiveDateTime);
}
}
Though it not solves your problem with existing LastActiveDateTime column, transformation will be applied only at moment of usage (in your view, most likely - anyways if you will try use it somewhere latter in query, it will not work for reason you already know), so no need to iterate manually.
Create View, which will transform data on server side; so your data will already be returned in format you need, if you're using DBFirst, probably, it's easiest and fastest solution;
Finally, you can use ToList() twice, once before selecting new ViewModel() (or call AsEnumerable(), or find other way to materialize query). It will fetch data from database and will allow you perform any C#-side functions you want directly in query after ToList(). But, as mentioned before - it's about getting all data, which matched criteria up to ToList() - in most cases it's not appropriate solution.
And here is some additional readings:
How can I call local method in Linq to Entities query?
I tested it in LINQpad and it works.
It still kinda iterates over users (with LINQ) but you don't have to add DateTime property to your viewmodel class. Also you could convert collection of Users to collection of UserViewModel objects with Automapper. It would still iterate over users of course but you wouldn't see it.
Had to create some setup code of course because I don't have your database.
void Main()
{
var db = new List<User> {
new User { LastActive = DateTime.Now, Username = "Adam", Lastname = "Nowak" },
new User { LastActive = DateTime.Now.AddYears(1), Username = "Eve", Lastname = "Kowalska"}
};
// select only properties that you need from database
var users = db
.Select(user => new { Username = user.Username, LastActive = user.LastActive})
.Distinct()
.ToList();
var usersVM = from u in users
select new UserViewModel { Username = u.Username, LastActiveDateTime = DateFriendly.GetFriendlyDate(u.LastActive)};
usersVM.Dump();
}
class User
{
public DateTime LastActive;
public string Username;
public string Lastname;
};
class UserViewModel
{
public string Username;
public string LastActiveDateTime;
}
static class DateFriendly
{
public static string GetFriendlyDate(DateTime date)
{
return "friendly date " + date.Year;
}
}
And this outputs
Username LastActiveDateTime
Adam friendly date 2013
Eve friendly date 2014
There is no direct Concert.ToDate method available for LINQ. But you can try using the DateAdd method from the SqlFunctions class:
var users = db.Relationships.Where(i => i.RelationshipId== relId)
.Select(s => new
{
s.User.Username,
LastActive=SqlFunctions.DateAdd("d",0, s.LastActive)
})
.ToList().Select(s => new UserViewModel()
{
Username = s.Username,
LastActiveDateTime = s.LastActive
});
Wouldn't the following work?
var users = db.Relationships.Where(i => i.RelationshipId== relId)
.Select(s => s.User).Distinct().Select(s => new UserViewModel() {
Username = s.Username,
LastActiveDateTime = DateFriendly.GetFriendlyDate(s.LastActive)
}).ToList();
I am using Entity Framework ExecuteStoreQuery to call a MySql stored procedure with output parameter. The store procedure also returns a result set.
I get the following error.
OUT or INOUT argument 2 for routine mydb.mysp is not a variable or NEW pseudo-variable in BEFORE trigger
What is wrong? Is there a better way to call MySql sp with mix of out params and resultset?
Here is the code
var outParam = new MySqlParameter();
outParam.ParameterName = "totalRowsOut";
outParam.MySqlDbType = MySqlDbType.Int64;
outParam.Direction = ParameterDirection.Output;
var data = db.ExecuteStoreQuery<PropertySearchResultRow>
(
"call mysp(#input1, #totalRowsOut);",
new MySqlParameter("input1", 10),
outParam
);
var result = data.ToList();
int totalRowsCount = (int)outParam.Value;
Finally ended up using the ado.net MySqlCommand to call the stored proc and get the out value. Mysql and EF integration is bad.
You just need to specify the name of the stored procedure and pass in the parameters. Something like this
var data = db.ExecuteStoreQuery<PropertySearchResultRow>
(
"mysp",new MySqlParameter("input1", 10),outParam
);
Here is my sample code working with VS2012 and Mysql connector 6.8.3 hope this helps someone
public static IList<MyClass> GetCustOrderHist(string someParameter)
{
IList<MyClass> data = ((IObjectContextAdapter)TestDashboardEntities).ObjectContext.ExecuteStoreQuery<MyClass>("CALL CustOrderHist({0});", someParameter).ToList();
return data;
}
public class MyClass
{
public string ProductName { get; set; }
public int TOTAL { get; set; }
}