I am using InMemoryDatabase for writing testcases, but when I want to test relational database operations(Insert/Update) I could not able to use InMemoryDatabase.
If I am using InMemoryDatabase I am getting an error like
System.InvalidOperationException: 'Relational-specific methods can only be used when the context is using a relational database provider.'
Is there any way to test relational database operations(Insert/Update) without touching the development or production database?
This is the repository class which uses the relational db operations
public bool UpdateOrInsert(Model model, Details details = null)
{
//_context is SqlDbContext
using (var transaction = _context.Database.BeginTransaction())
{
try
{
if(details.Id > 0)
{
//Update details;
_context.SaveChanges();
}
else
{
//Insert details;
_context.SaveChanges();
}
if(model.Id > 0)
{
//Update model;
_context.SaveChanges();
}
else
{
//Insert model;
_context.SaveChanges();
}
transaction.Commit();
return true;
}
catch(Exception ex)
{
transaction.Rollback();
throw;
}
}
}
How can we write unit test for this method without touching the real database? Is there any way?
The testcase I used to test this method throws the above error.
[Fact]
public void CheckTest()
{
DbContextOptions<SqlDbContext> _options;
_options = new DbContextOptionsBuilder<SqlDbContext>()
.UseInMemoryDatabase(databaseName: "DbName1")
.Options;
using (var context = new SqlDbContext(_options))
{
context.Model.Add(_model);//_model= sample model object
context.Details.Add(_details);//_details = sample details object
context.SaveChanges();
var cls = new ClassName();
var result = cls.UpdateOrInsert(_model,_details);
Assert.Assert.IsType<Bool>(result);
}
}
If I use the following way to check, it will be possible to update the original database.
[Fact]
public void CheckTest()
{
var connectionString = #"Data Source = *****; Initial Catalog = ******; uid = ****; Pwd = ******";
DbContextOptions<SqlDbContext> _sqlOptions = new DbContextOptionsBuilder<SqlDbContext>()
.UseSqlServer(connectionString)
.ConfigureWarnings(x => x.Ignore(InMemoryEventId.TransactionIgnoredWarning))
.Options;
using (var context = new SqlDbContext(_sqlOptions))
{
var cls = new ClassName();
var result = cls.UpdateOrInsert(_model,_details);
Assert.Assert.IsType<Bool>(result);
}
}
Do we have any other way to write test case for the method - UpdateOrInsert without touching the relational database?
Is it possible to test via InMemmoryDatabase?
Please help
Related
I am onboarding alone on an existing project that do not have any unit test. My first goal before any refactoring is to cover 100% of the code. I would like to avoid any regression.
I have read how do I mock sqlconnection or should I refactor the code? but my case as you can see below is quite different cause I need to do more than stub simply the sqlConnection. Basically, I need to mock the db. I would like to know the best approach to achieve it.
(By the way, I do not want to use any ORM such as Entity).
Below the code of the repository :
public class HotelRepository : IHotelRepository
{
private readonly IDbConnection _dbConnection;
private readonly ILogService _loggerService;
public HotelRepository(IDbConnection dbConnection, ILogService loggerService)
{
_dbConnection = dbConnection;
_loggerService = loggerService;
}
public HotelDo GetByRid(string rid)
{
return Find(rid).FirstOrDefault();
}
public List<HotelDo> Find(string text)
{
try
{
_dbConnection.Open();
var items = new List<HotelDo>();
using (var command = _dbConnection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "dbo.HotelSearchByRidOrName";
command.Parameters.Add(new SqlParameter("#Text", text));
using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection))
{
while (reader.Read())
{
items.Add(new HotelDo()
{
Name = SqlExtension.ReaderToStringConverter(reader["Name"]),
Id = SqlExtension.ReaderToIntConverter(reader["Id"]),
Rid = SqlExtension.ReaderToStringConverter(reader["RIDHotel"]),
IdPms = SqlExtension.ReaderToNullableIntConverter(reader["IdPms"]),
LinkResaWeb = SqlExtension.ReaderToStringConverter(reader["LinkResaWeb"]),
LinkPms = SqlExtension.ReaderToStringConverter(reader["LinkPms"]),
IdBrand = SqlExtension.ReaderToNullableIntConverter(reader["IdBrand"]) ?? 0,
IsOnline = SqlExtension.ReaderToBoolConverter(reader["IsOnline"]) ?? false,
CodeCountry = SqlExtension.ReaderToStringConverter(reader["CodeCountry"])
});
}
}
}
return items;
}
catch (Exception e)
{
var errorMessage = $"HotelRepository Find, text {text} ";
_loggerService.Trace(LogSeverity.Error, errorMessage, e);
throw new DalException() { Source = errorMessage, };
}
finally
{
_dbConnection.Close();
}
}
public List<HotelDo> GetAll()
{
try
{
_dbConnection.Open();
var items = new List<HotelDo>();
using (var command = _dbConnection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "dbo.HotelGetAll";
using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection))
{
while (reader.Read())
{
bool.TryParse(reader["IsOnline"].ToString(), out var isOnline);
items.Add(new HotelDo()
{
Id = SqlExtension.ReaderToIntConverter(reader["Id"]),
Rid = SqlExtension.ReaderToStringConverter(reader["RIDHotel"]),
Name = SqlExtension.ReaderToStringConverter(reader["Name"]),
CodeCountry = SqlExtension.ReaderToStringConverter(reader["CodeCountry"]),
LinkPms = SqlExtension.ReaderToStringConverter(reader["LinkPms"]),
IdPms = SqlExtension.ReaderToNullableIntConverter(reader["IdPms"]),
IdBrand = SqlExtension.ReaderToNullableIntConverter(reader["IdBrand"]) ?? 0,
LinkResaWeb = SqlExtension.ReaderToStringConverter(reader["LinkResaWeb"]),
IsOnline = isOnline
});
}
}
}
return items;
}
catch (Exception e)
{
var errorMessage = $"HotelRepository GetAllHotels";
_loggerService.Trace(LogSeverity.Error, errorMessage, e);
throw new DalException() { Source = errorMessage, };
}
finally
{
_dbConnection.Close();
}
}
}
Thank you for your help
So based on what you've got I've set up a test frame for you to follow. I've removed less important components for breviety.
Before you you jump in I just want to give my 2 cents, if you don't know how to do this sort of thing you seem to be more or less in a junior position or less experiance with C# and also found your self in a more or less mature company that doesn't care about the development deparment, since an uncovered project just get's thrown your way says you don't have alot of resources to go about to imrpove the code base.
Test only the things that have business value (can you put a price on the piece of logic if it brakes)
Delivering stuff faster will make you look better as a programmer (noone in the business gives a damn about test covarage)
Study hard, get your experiance, good reputation and don't be afraid to get the hell out of there as soon as you start getting bored.
Main
void Main()
{
var idIndex = 0;
var ids = new string[] { "1", "2" };
var mockDataReader = new Mock<IDataReader>();
mockDataReader.SetupSequence(x => x.Read()).Returns(true).Returns(true).Returns(false);
mockDataReader.SetupGet(x => x["Id"]).Returns(() => ids[idIndex]).Callback(() => idIndex++);
var mockParameters = new Mock<IDataParameterCollection>();
var mockCommand = new Mock<IDbCommand>();
mockCommand.SetupGet(x => x.Parameters).Returns(mockParameters.Object);
mockCommand.Setup(x => x.ExecuteReader(CommandBehavior.CloseConnection)).Returns(mockDataReader.Object);
var mockConnection = new Mock<IDbConnection>();
mockConnection.Setup(x => x.CreateCommand()).Returns(mockCommand.Object);
var repo = new HotelRepository(mockConnection.Object);
var result = repo.Find("search");
Assert.Equal("1", result[0].Id);
Assert.Equal("2", result[1].Id);
}
Repository
public class HotelRepository
{
private readonly IDbConnection _dbConnection;
public HotelRepository(IDbConnection dbConnection)
{
_dbConnection = dbConnection;
}
public List<Pony> Find(string text)
{
_dbConnection.Open();
var items = new List<Pony>();
using (var command = _dbConnection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "dbo.HotelSearchByRidOrName";
command.Parameters.Add(new SqlParameter("#Text", text));
using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection))
{
while (reader.Read())
{
items.Add(new Pony
{
Id = reader["Id"].ToString()
});
}
}
}
return items;
}
}
Just a dumb o'l pony
public class Pony {
public string Id { get; set; }
}
I'm new to unit testing.Can anyone explain me about how to done unit testing without hitting Database.
And also i want to know that, Is dependency injection essential for unit testing?
If yes, explain me with a sample code. It will be helpful for me.
I had already created unit testing for login, which hits Database. But i want to test the same case for the login, without hitting database.
In this, i used Microsoft.VisualStudio.TestTools.UnitTesting.
Here is my code,
[TestMethod]
public void _01_LoginUser_01_Valid()
{
BLUser.User.UserDTO user = new BLUser.User.UserDTO();
user.UserName = "mohan";
user.UserPassword = "abc";
BLUser.Model.Fs_User result = BLUser.User.LoginUser(user);
Assert.AreEqual("mohan", result.UserName);
}
And my business logic is,
public static Fs_User LoginUser(UserDTO userDTO)
{
try
{
var context = new UserDBEntities();
{
var LoginUser = context.Fs_User.Where(u => u.UserName == userDTO.UserName && u.UserPassword == userDTO.UserPassword).SingleOrDefault();
if (LoginUser == null)
ValidationError.LoginException((int)ExceptionCodes.InvalidPassword);
return LoginUser;
}
}
catch (Exception ex)
{
throw ex;
}
}
See my answer here: Solid Principle examples anywhere?
If you look at the idea of depending on an interface for your data access, you'll see how you could supply a 'fake' implementation of that interface for testing that would not depend on a database.
Based on your example, you need to make something like the following changes:
public class LoginThing
{
private readonly IAmSomeContext context;
public LoginThing(IAmSomeContext context)
{
this.context = context;
}
public Fs_User LoginUser(UserDTO userDTO)
{
try
{
var LoginUser =
this.context.Fs_User
.SingleOrDefault(
u =>
u.UserName == userDTO.UserName
&& u.UserPassword == userDTO.UserPassword);
if (LoginUser == null)
ValidationError.LoginException((int)ExceptionCodes.InvalidPassword);
return LoginUser;
}
catch (Exception ex)
{
throw ex;
}
}
}
And then your test can become something like:
[TestMethod]
public void _01_LoginUser_01_Valid()
{
BLUser.User.UserDTO user = new BLUser.User.UserDTO();
user.UserName = "mohan";
user.UserPassword = "abc";
var fakeContext = CreateFakeContextWith(user);
var thingUnderTest = new LoginThing(fakeContext);
BLUser.Model.Fs_User result = thingUnderTest.LoginUser(user);
Assert.AreEqual("mohan", result.UserName);
}
Creating the fake context would look something like this, if you used NSubstitute:
private IAmSomeContext CreateFakeContextWith(BLUser.User.UserDTO user)
{
var fakeContext = Substitute.For<IFakeContext>();
fakeContext.Fs_User.Returns(new List(new[] {user}));
return fakeContext;
}
The syntax might not be exact, but you get the point...
I need to unit testing this GetData method.
public MessageResponse GetData(XmlElement requestElement)
{
MessageResponse MsgResponse = new MessageResponse();
if (requestElement.Attributes["employeeNo"] == null){
MsgResponse.Messages = new List<string>();
MsgResponse.Messages.Add("Attribute employeeNo is missing");
MsgResponse.Error = true;
return MsgResponse;
}
if (requestElement.Attributes["xmlEmployeeName"] == null){
MsgResponse.Messages.Add("Attribute xmlEmployeeName is missing");
MsgResponse.Error = true;
return MsgResponse;
}
return MsgResponse;
}
this method needs a XmlElement parameter. how do I mock it? in my code, I first created a xmlDocument, then load the xml file.
XmlDocument doc = new XmlDocument();
doc.Load(xmlFilePath);
requestElement = doc.DocumentElement;
for me to test it, first i need to create a xml file without employeeNo, the create antoher one without name, maybe alot more for other scenarios. it just seems like alot work. is there a better way to test it?
should I use moq or other testing framework to simplify the testing?
You can just create the element you want to test with, w/o reading a file at all:
var doc = new XmlDocument();
doc.LoadXml("<MyTestElement/>");
var myTestElement = doc.DocumentElement;
myTestElement.Attributes["employeeNo"] = "fakeId";
var response = myTestResponder.GetData(myTestElement);
//assert whatever you need to
NOTE: every time you find out that the test is too hard to write, usually this means that your class/method does too much.
I would assume, that your method verifies the input, than does something with the data provided. I would suggest that you abstract the data reading part (using some xml deserializer) to populate the data model you need for your application.
Then run validation on the result of the deserialized data. Something like:
public MessageResponse GetData(XmlElement requestElement)
{
var data = _xmlDeserializer.Deserialize(requestElement);
var validationResult = _validator.Validate(data);
if (validationResult.Errors.Count > 0)
{
//populate errors
return result;
}
_dataProcessor.DoSomethingWithData(data);
}
Take a look at FluentValidation for a nice validation library.
If you go the above route, then your tests will be much simpler.
[TestMethod]
public void GetData_Returns_Correct_Message_When_EmployeeNo_Is_Null()
{
var inputWithoutEmployeeNo = GetElement(#"<input></input>");
var actual = GetData(inputWithoutEmployeeNo);
Assert.IsTrue(actual.Error, "Error should be true when employee no. is missing");
Assert.IsNotNull(actual.Messages);
Assert.AreEqual(1, actual.Messages.Count);
Assert.AreEqual("Attribute employeeNo is missing", actual.Messages[0]);
}
private XmlElement GetElement(string xml)
{
var doc = new XmlDocument();
doc.LoadXml(xml);
return doc.DocumentElement;
}
While working on the unit test, I found out that the code throws a NullReferenceException.
The following unit test demonstrates the issue:
[TestMethod]
[ExpectedException(typeof(NullReferenceException))]
public void GetData_Throws_NullReferenceException_When_EmployeeNo_Is_Not_Null_And_XmlEmployeeName_Is_Null()
{
var inputWithoutEmployeeNo = GetElement(#"<input employeeNo='123'></input>");
GetData(inputWithoutEmployeeNo);
}
Using Moq
using System;
using System.Xml;
using Moq;
using NUnit.Framework;
namespace MockXmlTest
{
[TestFixture]
public class MyServiceTests
{
private MockSetup _mockSetup;
[SetUp]
public void Init()
{
_mockSetup = MockSetup.HappySetup();
}
[Test]
public void MyService_Should_Return_Guid()
{
//Arrange
var myService = _mockSetup.MyService.Object;
var id = 42;
var expected = Guid.Empty.ToString();
//Act
var actual = myService.GetXml(id);
//Assert
Assert.AreEqual(expected, actual.FirstChild.InnerText);
}
}
public class MyService : IMyService
{
public XmlDocument GetXml(int id)
{
var doc = new XmlDocument();
//Do real stuff
return doc;
}
}
public interface IMyService
{
XmlDocument GetXml(int id);
}
public class MockSetup
{
public Mock<IMyService> MyService { get; set; }
public MockSetup()
{
MyService = new Mock<IMyService>();
}
public static MockSetup HappySetup()
{
var mockSetup = new MockSetup();
var mockDoc = CreateMockDoc();
//Matches any id of an integer, returns a XmlDocument mock
mockSetup.MyService.Setup(m => m.GetXml(It.IsAny<int>())).Returns(mockDoc);
return mockSetup;
}
private static XmlDocument CreateMockDoc()
{
//<Main><MyGuid>00000000-0000-0000-0000-000000000000</MyGuid></Main>
XmlDocument mockDoc = new XmlDocument();
XmlElement el = (XmlElement)mockDoc.AppendChild(mockDoc.CreateElement("Main"));
el.AppendChild(mockDoc.CreateElement("MyGuid")).InnerText = It.IsAny<Guid>().ToString();
return mockDoc;
}
}
}
I am writing a set of integration tests (Unit tests with MS Test which test that Entity Framework 4.2 is persisting all classes correctly to the database).
When I run all tests one by one they all work fine. When I run them in a group - some of them fail as the wrong number of objects are returned - it would seem that the db is being cleaned down once at the start of the tests and not in between each test - even though I can see a new context being created and then disposed of for each test
Any Ideas?
public class EmptyDataInitializer : DropCreateDatabaseAlways<myContext>
{
protected override void Seed(myContext db)
{
//Do Nothing Create Empty Database
db.SaveChanges();
base.Seed(db);
}
}
A Cut down version of the unit/integration Tests
[TestClass]
public class PersistanceTests
{
//Creating two instances of our Repository so that we can make sure that we are reading from our database rather than in-memory
private myContext _db;
private myContext _dbResults;
private readonly ISettings _configSettings;
public PersistanceTests()
{
_configSettings = MockRepository.GenerateStub<ISettings>();
_configSettings.ConnectionString = "data source=.;initial catalog=myContext_Test; Integrated Security=SSPI; Pooling=false";
Database.SetInitializer(new EmptyDataInitializer());
}
//This is called a single time after the last test has finished executing
[TestCleanup]
public void TearDownTest()
{
_db.Dispose();
_db = null;
_dbResults.Dispose();
_dbResults = null;
}
//This is called each time prior to a test being run
[TestInitialize]
public void SetupTest()
{
_db = new myContext(_configSettings);
_dbResults = new myContext(_configSettings);
// This forces the database to initialise at this point with the initialization data / Empty DB
var count = _db.Accounts.Count();
var resultCount = _dbResults.Accounts.Count();
if (count != resultCount) throw new InvalidOperationException("We do not have a consistant DB experiance.");
}
[TestMethod]
public void OrganisationPersistanceTest()
{
// Arrange
var apple = new Organisation { Name = "Apple" };
_db.Organisations.Add(apple);
// Act
_db.SaveChanges();
var organisationsCount = _dbResults.Organisations.Count();
var organisationsAppleCount = _dbResults.Organisations.Where(a => a.Id == apple.Id).Count();
var result = _dbResults.Organisations.FirstOrDefault(a => a.Id == apple.Id);
// Assert
Assert.IsTrue(organisationsCount == 1, string.Format("Organisations Count Mismatch - Actual={0}, Expected={1}", organisationsCount, 1));
Assert.IsTrue(organisationsAppleCount == 1, string.Format("Apple Organisations Count Mismatch - Actual={0}, Expected={1}", organisationsAppleCount, 1));
Assert.IsNotNull(result, "Organisations Result should not be null");
Assert.AreEqual(result.Name, apple.Name, "Name Mismatch");
}
//A Unit test
[TestMethod]
public void OrganisationWithNumberOfPeople_PersistanceTest()
{
// Arrange
var person = new Person { Firstname = "Bea" };
var anotherPerson = new Person { Firstname = "Tapiwa" };
var apple = new Organisation { Name = "Apple" };
apple.AddPerson(person);
apple.AddPerson(anotherPerson);
_db.Organisations.Add(apple);
// Act
_db.SaveChanges();
var organisationsCount = _dbResults.Organisations.Count();
var organisationsAppleCount = _dbResults.Organisations.Where(a => a.Id == apple.Id).Count();
var result = _dbResults.Organisations.FirstOrDefault(a => a.Id == apple.Id);
var peopleCountInOrganisation = result.People.Count();
// Assert
Assert.IsTrue(organisationsCount == 1, string.Format("Organisations Count Mismatch - Actual={0}, Expected={1}", organisationsCount, 1));
Assert.IsTrue(organisationsAppleCount == 1, string.Format("Apple Organisations Count Mismatch - Actual={0}, Expected={1}", organisationsAppleCount, 1));
Assert.IsNotNull(result, "Organisations Result should not be null");
Assert.AreEqual(result.People.Count, peopleCountInOrganisation, "People count mismatch in organisation Apple - Actual={0}, Expected={1}", peopleCountInOrganisation, 2);
Assert.AreEqual(result.Name, apple.Name, "Name Mismatch");
}
}
Stepping through the tests I can see the SetupTest and TearDownTest methods being called but I it does not seem to clean down the database between tests.
Okay even Better answer - add a database.Initialize(force: true);
into the TestInitialize method.
[TestInitialize]
public void SetupTest()
{
_db = new myContext(_configSettings);
_db.Database.Initialize(force: true);
_dbResults = new myContext(_configSettings);
// This forces the database to initialise at this point with the initialization data / Empty DB
var count = _db.Accounts.Count();
var resultCount = _dbResults.Accounts.Count();
if (count != resultCount) throw new InvalidOperationException("We do not have a consistant DB experiance.");
}
I use a helper for doing this kind of tasks:
public abstract class TestingHelper
{
public static void ClearDatabase()
{
DatabaseContext myDbContext = new DatabaseContext();
myDbContext.Database.Delete();
myDbContext.Database.Create();
//FillDatabase(lawyers); //<- OPTIONAL if you want to add rows to any type tables
}
}
And then use it in the SetUp of your test:
[SetUp]
public void MyTests_SetUp()
{
TestingHelper.ClearDatabase();
}
I want to create test case for below method "GetByEmail".
public User GetByEmail(string email, bool includeUserRoles = false, bool includeUserType = false)
{
Expression<Func<User>> whereClause = u => u.Email == email;
return GetQuery(whereClause, includeUserRoles, includeUserType) .FirstOrDefault();
}
private IQueryable<User> GetQuery(Expression<Func<User>> whereClause,
bool includeUserRoles = false, bool includeUserType = false)
{
IQueryable<User> query = base.GetQuery(whereClause);
if (includeUserRoles)
query = query.Include(u => u.UserRoles);
if (includeUserType)
query = query.Include(u => u.UserType);
return query;
}
protected IQueryable<T> GetQuery<T>(Expression<Func<T>> predicate) where T : EntityBase
{
return predicate != null ?
CreateObjectSet<T>().Where(predicate) :
CreateObjectSet<T>();
}
protected IObjectSet<T> CreateObjectSet<T>() where T : EntityBase
{
return _context.CreateObjectSet<T>();
}
public static IQueryable<T> Include<T>(this IQueryable<T> source, Expression<Func<T>> property)
{
var objectQuery = source as ObjectQuery<T>;
if (objectQuery != null)
{
var propertyPath = GetPropertyPath(property);
return objectQuery.Include(propertyPath);
}
return source;
}
Below is my test case method -
[Fact]
private void GetByEmail_PassedEmailAddress_RelatedUser()
{
//Created fake context
var fakeContext = Isolate.Fake.Instance<Entities>();
//Created fake Repository and passed fakeContext to it
var fakeRepository = Isolate.Fake.Instance<Repository>(Members.CallOriginal, ConstructorWillBe.Called, fakeContext);
//Created fake in memory collection of User
var fakeUsers = GetUsers();
Isolate.WhenCalled(() => fakeContext.Context.CreateObjectSet<User>())
.WillReturnCollectionValuesOf(fakeUsers);
var User = Isolate.Invoke.Method(fakeRepository, "GetByEmail", "abc#xyz.com", true, true);
Assert.True(User != null);
}
In the above test case method I successfully get the user with passed email but not able to include other entities of associated user.
Kindly let me know, how can I include other entities with associated User.
Include is leaky abstraction - it works only with EF and linq-to-entities and cannot be successfully used with linq-to-objects. You know that your unit test needs populated relations so your GetUsers method must prepare that data. That is a point of mocking / faking - you don't think about internal implementation of mocked method. You simply return what should be returned.
Btw. what is the point of your test? It looks like you are trying to test a mock - that is wrong. Mock provides correct data and you only need it to test another feature dependent on mocked component.