Reputation: 53
I am new with Unit Testing, would be happy to know if I am making any mistake or not proceeding the right direction.
Here is the situation:
I am trying to test a method (MethodUnderTest
) which calls another method(MethodWithAction
) which takes Action<T>
as argument.
I want to mock MethodWithAction
, but test the logic based on the return value.
Here is the structure:
interface IInterface
{
void MethodWithAction(Action<string> action);
}
class MyClass : IInterface
{
public void MethodWithAction(Action<string> action)
{
string sampleString = "Hello there";
action(sampleString);
}
}
class ClassUnderTest
{
public IInterface Obj = new MyClass();
public string MethodUnderTest()
{
string stringToBeTested = string.Empty;
Obj.MethodWithAction(str =>
{
if (str.Contains("."))
stringToBeTested = string.Empty;
else
stringToBeTested = str.Replace(" ", string.Empty);
});
return stringToBeTested;
}
}
My test method goes like this:
[TestMethod]
[DataRow("Hello, World", "Hello,World")]
[DataRow("Hello, World.","")]
[DataRow("Hello", "Hello")]
public void MethodUnderTestReturnsCorrectString(string sampleString, string expected)
{
var mockObj = new Mock<IInterface>();
mockObj.Setup(m=>m.MethodWithAction(It.IsAny<Action<string>>))
.Callback(???);
ClassUnderTest sut = new ClassUnderTest();
sut.Obj=mockObj.Object;
string actual = sut.MethodUnderTest();
Assert.Equal(expected, actual);
}
I would like to know what goes at the place of ???
in the test, or is there entirely different approach for this problem?
Upvotes: 3
Views: 3627
Reputation: 1099
My first instinct would be to refactor ClassUnderTest
and IInterface
so that IInterface
has a get property such that you remove the dependency of the IInterface implementation entirely and MyClass has only the one job to do (store the SampleString):
interface IInterface
{
string SampleString { get; }
}
// Fix MyClass
class MyClass : IInterface
{
public string SampleString => "Hello There"
}
class ClassUnderTest
{
public string MethodUnderTest(IInterface someObject)
{
string stringToBeTested = string.Empty;
if (someObject.SampleString.Contains("."))
stringToBeTested = string.Empty;
else
stringToBeTested = str.Replace(" ", string.Empty);
return stringToBeTested;
}
}
So we can remove the Action altogether and the code is somewhat more readable and easier to follow when testing.
Just another way of looking at the problem.
Upvotes: 0
Reputation: 247018
Grab the action parameter that was passed to the mock in the call back and invoke it with the sample string.
mockObj
.Setup(m => m.MethodWithAction(It.IsAny<Action<string>>))
.Callback((Action<string> action) => action(sampleString));
Reference Moq Quickstart to get a better understanding of how to use this mocking framework.
Upvotes: 6