Reputation: 33
I am attempting to write unit tests for an existing .NET MVC 4.5 web application that uses Entity Framework. I'd like to test that a bool
method returns true
/false
as expected; however the method under test writes to a database log.
Unfortunately, these tests throw the following exception:
System.InvalidOperationException: No connection string named 'xxxDbContext' could be found in the application config file.
I do not want the database to be modified, and I'm struggling to find a sample of how to mock the `DbContext in this example.
How can I test only the boolean results without actually writing to the database? In the sample below, Logger.Log()
adds a row to the database, and I don't want the actual database being modified during unit tests.
Here are simplified examples of what the code looks like:
[TestMethod]
public void LocalFileCompare_File2IsNull_ReturnsFalse()
{
var t = new tManager(new tSet());
FileInfo file1 = new FileInfo(@"C:\Temp\TempFile.txt");
FileInfo file2 = null;
var result = transferSet.LocalFileCompare(file1, file2);
Assert.IsFalse(result);
}
public bool LocalFileCompare(FileInfo file1, FileInfo file2)
{
if (file1 == null || file2 == null)
{
Logger.Log("LocalFileCompare: One or both files are null.");
return false;
}
if (file1.FullName != file2.FullName)
{
Logger.Log($"LocalFileCompare - file names don't match.");
return false;
}
if (file1.Length != file2.Length)
{
Logger.Log($"LocalFileCompare - file sizes don't match.");
return false;
}
return true;
}
Thank you!
Upvotes: 2
Views: 509
Reputation: 11963
If you just want a quick and dirty solution without having to use dependency injection you could use Preprocessor
something like
public void Log()
{
#if UnitTest
//Log to file
#else
//Log to db
#endif
}
and then add a new configuration name UnitTest
. Switch to that config and go to project property -> Build -> Conditional compilation symbols -> add UnitTest
in
Upvotes: 0
Reputation: 3777
Your LocalFileCompare()
method depends on the Logger
class (a.k.a. it is "tightly coupled"). Because the implementation of Logger
writes to the database, you cannot avoid writing to the database when you call LocalFileCompare()
unless you remove the dependency. You do that by instead making the method depend on an interface, make the Logger
class implement that interface, and then "inject" it into the LocalFileCompare()
method.
This will allow you to easily create a mock logger class by simply implementing the interface and having the implementation not write to the database.
Step 1: Create an ILogger
interface
Step 2: Change your Logger
class to implement ILogger
Step 3: Change the signature of your LocalFileCompare()
method to include an ILogger
parameter
Step 4: Add the Logger
instance to all calls to LocalFileCompare()
method
Step 5: Update your test(s) with a mocked ILogger
implementation
// Step 1
public interface ILogger
{
void Log(string message);
}
// Step 2
public class Logger : ILogger
{
void Log(string message)
{
// Implementation here
}
}
// Step 3
public bool LocalFileCompare(FileInfo file1, FileInfo file2, ILogger logger)
{
if (file1 == null || file2 == null)
{
logger.Log("LocalFileCompare: One or both files are null.");
return false;
}
if (file1.FullName != file2.FullName)
{
logger.Log($"LocalFileCompare - file names don't match.");
return false;
}
if (file1.Length != file2.Length)
{
logger.Log($"LocalFileCompare - file sizes don't match.");
return false;
}
return true;
}
// Step 4
// Update your calls to LocalFileCompare()
// Step 5
// Put this somewhere in the Test project
public class MockLogger : ILogger
{
void Log(string message)
{
Console.WriteLine(message);
}
}
// In your test class
ILogger mockLogger;
// In your Test Setup method
mockLogger = new MockLogger();
[TestMethod]
public void LocalFileCompare_File2IsNull_ReturnsFalse()
{
var t = new tManager(new tSet());
FileInfo file1 = new FileInfo(@"C:\Temp\TempFile.txt");
FileInfo file2 = null;
var result = transferSet.LocalFileCompare(file1, file2, mockLogger);
Assert.IsFalse(result);
}
Upvotes: 1