acoy
acoy

Reputation: 33

Prevent writing to database on unit tests - .NET MVC / Entity Framework

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:

Test method

[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);
}

Method under test

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

Answers (2)

Steve
Steve

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

Lews Therin
Lews Therin

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

Related Questions