Qwadrox
Qwadrox

Reputation: 808

Nunit -Check method was called with a parameter

The test should test the AddDebug method works correctly. So If the username is null It will automatically set the user name to "Anononymus". However I can't see the List because it's private, therefore the only way to check to function works correctly is take advantage of that opportunity that the AddDebug method calling the Add method. I have to verify that the Add method was called with the "Anonymous" parameter I only can use Nunit, Mock is not optional now for me.

private List<Log> Entries;
    public Logger()
    {
        Entries = new List<Log>();
    }
    public void Add(string text, LogLevels level, DateTime timeStamp, string userName)
    {
        var Enitity = new Log();
        Enitity.Text = text;
        Enitity.Level = level;
        Enitity.TimeStamp = timeStamp;
        Enitity.UserName = userName;
        Add(Enitity);

    }
    public void Add(Log log)
    {
        if (!(log.TimeStamp > DateTime.Now))
        {
            Entries.Add(log);
        }
    }
    public void AddDebug(string text, string userName = null)
    {
        if (String.IsNullOrEmpty(userName) == true)
        {
            Add(text, LogLevels.Debug, DateTime.Now, "Anonymus");
        }
        else
        {
            Add(text, LogLevels.Debug, DateTime.Now, userName);
        }

    }

Upvotes: 3

Views: 2816

Answers (2)

weichch
weichch

Reputation: 10045

Given the facts:

  • you cannot edit Logger class which is not unit test friendly
  • the implementation does not expose Entries as public information, nor it could be passed into Logger as parameter
  • none of the methods is virtual.

You have to use Reflection to get Entries.

var logger = new Logger();
logger.AddDebug("some text", userName: null);

// Get Entries in logger
var entriesField = typeof(Logger).GetField(
    "Entries", BindingFlags.NonPublic | BindingFlags.Instance);
var entries = (List<Log>)entriesField.GetValue(logger);
var containsAnonymous = entries.Any(e => e.UserName == "Anonymus");

However, this is usually / arguably not considered as a good practice in unit testing, as this relies on the internals of Logger class and it is likely that if you change the implementation of Logger, this unit test can break.

I would still recommend using one of @user8606929's solutions if it is possible.

Upvotes: 0

user8606929
user8606929

Reputation: 225

I would recommend that you use a Mocking Framework like Moq. See my attached example code. ILog is the Interface for Log Class.

public interface ILog {
 void Add(Log log);
}

Your Logger class could look like this:

public class Logger : ILog{
....
public void Add(Log log)
   {
       if (!(log.TimeStamp > DateTime.Now))
        {
            Entries.Add(log);
        }
   }
}
...

You can design your TestCases like this

using Moq;

public class TestLogger 
{
  private Mock<ILog> _logMock;
  private ILog _logger;


  [Setup]
  public void SetUp()
  {

  _logMock = new Mock<ILog>();
  _logMock.Setup(a => a.Add(It.IsAny<Log>())).Verifiable(); // check if method is called

  _logger = _logMock.Object;          
  }

...

  [Test]
  public void SomeTest()
  {
   //test some business logik - inject your logger instance into businesslogic class

   //check if logger was called once
  _logMock.Verify(a => a.Add(It.IsAny<Log>()), Times.Once); 
  }

}


If its not possible to use Moq and external interfaces, you can write your test to check if your Entries List is not empty.

public class Logger
{
  public List<Log> Entries = new List<Log>();

  ...
}
[TestCaseSource(nameof(something))]
    public void AddDebugSetAnonymusUserTest()
    {
        var logger = new Logger();
        logger.AddDebug("Text", null);

       //here is the check if AddDebug was successful
       Assert.AreEqual(1, logger.Entries.Count);

       //check if user was anonymous
       Assert.AreEqual("Anonymous", logger.Entries.FirstOrDefault().UserName);
    }

....

Upvotes: 2

Related Questions