I am new to unit testing, so pardon me if I am unable to explain this question properly. I am reading a book "The art of Unit Testing 2nd Edition" and trying to implement unit testing in my project. I am currently stuck or confused when testing using mocking (using NSubstitute as the mocking framework).
Here is my scenario:
I have two interfaces ICommand
and IUser
public interface ICommand
{
string execute();
}
public interface IUserCalendar
{
string LoadCalendar();
}
I have a class LoadCalendar
which implements ICommand
:
public class LoadCalendar : ICommand
{
private IUserCalendar user;
public string execute()
{
return this.user.LoadCalendar();
}
public LoadCalendar(IUserCalendar obj)
{
this.user = obj;
}
}
ViewCalendar
implements IUserCalendar
:
public class Viewer : IUserCalendar
{
public string LoadCalendar()
{
return "Viewer Load Calendar Called";
}
}
Using an agent class I am invoking command for specific request. (Here I am showing only one request LoadCalendar
for one user viewer but I have more command and more users)
My client has an invoker object that invokes the command for specific user.
public class Client
{
public Client()
{ }
public string LoadCalendar(ICommand cmd)
{
Invoker invoker = new Invoker(cmd);
return invoker.execute();
}
}
Now I like to test the client class that when it calls for specific user it should return proper object or message.
[Test]
public void client_Load_Calendar_Administrator()
{
IUserCalendar calanedar = Substitute.For<IUserCalendar>();
ICommand cmd = Substitute.For<ICommand>(calanedar);
Client c = new Client();
c.LoadCalendar(cmd, calanedar).Returns(Arg.Any<string>());
}
I don't know where I am doing wrong and it's throwing an error.
NSubstitute.Exceptions.SubstituteException : Can not provide constructor arguments when substituting for an interface.
Any help is really appreciated. Sorry for long question.
The error you're getting:
Can not provide constructor arguments when substituting for an interface.
Is telling you exactly what's wrong.
You're passing in constructor arguments here:
ICommand cmd = Substitute.For<ICommand>(calanedar);
Of course, interfaces never have a constructor. You're trying to interact with your ICommand interface as if it were your concrete LoadCalendar
implementation of it.
Furthermore, to be able to unit test a class you always want to have a default (parameterless) constructor. Many mocking frameworks actually require this.
In this case you should probably test against the concrete class and mock/substitute the classes that it uses.
Either that, or you only substitute ICommand
simply to have it return a pre-set (string) value. Then you can proceed to verify if the code that consumes your command, actually invokes it and/or does the correct thing with the value it returns.
To illustrate:
[Test]
public void client_Load_Calendar_Administrator()
{
// You are substituting (mocking) the IUserCalendar here, so to test your command
// use the actual implementation
IUserCalendar calendar = Substitute.For<IUserCalendar>();
ICommand cmd = new LoadCalendar(calendar):
// Let the IUserCalendar.LoadCalendar() return a certain string
// Then Assert/Verify that cmd.Execute() returns that same string
}
That's the point of unit testing: you test the smallest piece of functionality by mocking all dependencies. Otherwise it's an integration test.
To test your client:
[Test]
public void client_Load_Calendar_Administrator()
{
ICommand cmd = Substitute.For<ICommand>();
Client c = new Client();
// Let your command return a certain string
// Then verify that your calendar returns that same string
}
EDIT: In case you're interested, the method in NSubstitute that throws this exception :
private void VerifyNoConstructorArgumentsGivenForInterface(object[] constructorArguments)
{
if (constructorArguments != null && constructorArguments.Length > 0)
{
throw new SubstituteException("Can not provide constructor arguments when substituting for an interface.");
}
}
They're pretty clear about it: no constructor arguments for an interface substitute, no matter what.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.