1

I would like to test if execution invokes a method with string that contains given part AND an instantiation of class that contains (on it's constructor) part of given string.

[TestMethod]
public void TestMethod1()
{
    // Arrange
    var fakeLogger = A.Fake<ILogger>();
    var loggerExecutor = new LoggerExecutor(fakeLogger);

    // Act
    loggerExecutor.Log();

    // Assert
    A.CallTo(() => fakeLogger.Error(A<string>.That.Contains("string")).MustHaveHappenedOnceExactly();
    A.CallTo(() => fakeLogger.Error(
        A.Fake<LogLine>(x => x.WithArgumentsForConstructor(() => A<string>.That.Contains("LogLine"))))
    ).MustHaveHappenedOnceExactly();
}

public class LoggerExecutor
{
    private readonly ILogger _logger;

    public LoggerExecutor(ILogger logger)
    {
        _logger = logger;
    }

    public void Log()
    {
        _logger.Error("With string parameter");
        _logger.Error(new LogLine("With LogLine parameter"));
    }
}

The first assert with string object works. But, when I'm trying to test the second assert, it fails with the following exception:

System.InvalidOperationException: An argument constraint, such as That, Ignored, or _, cannot be nested in an argument.
at FakeItEasy.Expressions.ExpressionArgumentConstraintFactory.ArgumentConstraintExpressionVisitor.VisitMember(MemberExpression node)

I should try with fake object to avoid repeat all string on tests.

LogLine class:

public class LogLine
{
    public readonly string Message;

    public readonly object[] Params;

    public static implicit operator LogLine(string message)
    {
        return new LogLine(message ?? string.Empty);
    }

    public LogLine(string message, params object[] @params)
    {
        Message = message;
        Params = @params;
    }
}

Tested with:

  • FakeItEasy v6.0.1
  • MSTest.TestAdapter v2.1.1
  • MSTest.TestFramework v2.1.1
fiskolin
  • 1,421
  • 2
  • 17
  • 36

1 Answers1

2

FakeItEasy can't tell how an object passed to a method was constructed. The second _logger.Error invocation is passed an instance of LogLine, so I think you want to examine that object itself. Specifically, you can look at the Message property.

Try

A.CallTo(() => fakeLogger.Error(
        A<LogLine>.That.Matches(ll => ll.Message.Contains("LogLine")))
    ).MustHaveHappenedOnceExactly();
Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
  • 2
    Super glad of being helped by you, sir Blair Conrad. Many thanks for the answer and for the community work around FakeItEasy. – fiskolin Apr 14 '20 at 16:27