I always had a problem when unit testing classes that calls other classes, for example I have a class that creates a new user from a phone-number then saves it to the database and sends a SMS to the number provided.
Like the code provided below.
public class UserRegistrationProcess : IUserRegistration
{
private readonly IRepository _repository;
private readonly ISmsService _smsService;
public UserRegistrationProcess(IRepository repository, ISmsService smsService)
{
_repository = repository;
_smsService = smsService;
}
public void Register(string phone)
{
var user = new User(phone);
_repository.Save(user);
_smsService.Send(phone, "Welcome", "Message!");
}
}
It is a really simple class but how would you go about and test it?
At the moment im using Mocks but I dont really like it
[Test]
public void WhenRegistreringANewUser_TheNewUserIsSavedToTheDatabase()
{
var repository = new Mock<IRepository>();
var smsService = new Mock<ISmsService>();
var userRegistration = new UserRegistrationProcess(repository.Object, smsService.Object);
var phone = "07012345678";
userRegistration.Register(phone);
repository.Verify(x => x.Save(It.Is<User>(user => user.Phone == phone)), Times.Once());
}
[Test]
public void WhenRegistreringANewUser_ItWillSendANewSms()
{
var repository = new Mock<IRepository>();
var smsService = new Mock<ISmsService>();
var userRegistration = new UserRegistrationProcess(repository.Object, smsService.Object);
var phone = "07012345678";
userRegistration.Register(phone);
smsService.Verify(x => x.Send(phone, It.IsAny<string>(), It.IsAny<string>()), Times.Once());
}
It feels like I am testing the wrong thing here?
Any thoughts on how to make this better?
Refactoring the mocks out in the way that #Serghei suggests is good.
I also see that the name of the behaviour isn't actually describing the behaviour. I like to use the word "should", as in, "My class should do some stuff".
Your class shouldn't send the user to the database when it's registering a user. It should ask the repository to save the user. That's all. It doesn't know whether the repository sends it to the database, keeps it in memory or nukes it from orbit. It's not your class's responsibility.
By phrasing the behaviour this way, you can explicitly show - and help others understand - where the scope of your class's responsibility ends.
If you rename your method something like WhenRegisteringANewUser_AsksRepositoryToSaveIt() that might make the example you've given feel more natural.
In your case don't need to write
repository.Verify(x => x.Save(It.Is<User>(user => user.Phone == phone)), Times.Once());
because this method doesn't return a value
you can write
repository.VerifyAll();
Also for smsService it's a good way to use Moq.
look at after some refactor
Mock<IRepository<>> repository;
private Mock<ISmsService> smsService;
const string phone = "0768524440";
[SetUp]
public void SetUp()
{
repository = new Mock<IRepository<>>();
smsService = new Mock<ISmsService>();
}
[Test]
public void WhenRegistreringANewUser_TheNewUserIsSavedToTheDatabase()
{
var userRegistration = new UserRegistrationProcess(repository.Object, smsService.Object);
userRegistration.Register(phone);
repository.VerifyAll();
smsService.VerifyAll();
}
Related
I'm using NSubstitute in an integration test by wrapping a real implementation with a mock, like this:
var realRepository = container.Get<IRepository>();
var proxyRepository = Substitute.For<IRepository>();
proxyRepository
.When(repo => repo.InsertValueForEntity(Arg.Any<int>(), Arg.Any<ValueForEntity>())
.Do(callInfo => realRepository
.InsertValueForEntity((int)callInfo.Args()[0], (ValueForEntity)callInfo.Args()[1]));
proxyRepository
.GetValueForEntity(Arg.Any<int>())
.Returns(callInfo => realRepository
.GetValueForEntity((int)callInfo.Args()[0]));
// assume these parameters are defined elsewhere
var factory = new EntityFactory(mock1, realDependency, mock2, proxyRepository);
var entity = factory.CreateEntity(/* args */); // <-- this is where proxyRepository.GetValueForEntity() should be called
proxyRepository.Received().InsertValueForEntity(entity.Id, dummyValue);
proxyRepository.Received().GetValueForEntity(Arg.Is(entity.Id));
Assert.That(entity.Value, Is.EqualTo(dummyValue));
What's strange to me about this is that I have another test using the .When().Do() technique like this, and it works just fine. And indeed it appears that the configuration for InsertValueForEntity works here also. However, the configuration for GetValueForEntity is not working, and I don't understand why. I put a breakpoint in the lambda and it never hits.
Is there something tricky about substitutes that I'm missing here?
There does not seem any obvious problem with the example code, so I am guessing it is an issue with some of the code not shown. Is it possible to post a runnable version that illustrates the problem? I also suggest adding NSubstitute.Analyzers to your project as it can help detect issues that sometimes causing confusing test behaviour.
Here's a simplified version that demonstrates everything working correctly. If it is possible to modify this to reproduce the problem that would be really helpful!
First, some supporting types:
public interface IRepository {
ValueForEntity GetValueForEntity(int v);
void InsertValueForEntity(int v, ValueForEntity valueForEntity);
}
public class RealRepository : IRepository {
private readonly IDictionary<int, ValueForEntity> data = new Dictionary<int, ValueForEntity>();
public ValueForEntity GetValueForEntity(int v) => data[v];
public void InsertValueForEntity(int v, ValueForEntity valueForEntity) => data[v] = valueForEntity;
}
public class ValueForEntity {
public int Id { get; set; }
}
Then a rough approximation of the subject being tested:
public class EntityFactory {
private readonly IRepository repo;
public EntityFactory(IRepository repo) => this.repo = repo;
public ValueForEntity CreateEntity(int id) {
repo.InsertValueForEntity(id, new ValueForEntity { Id = id });
return repo.GetValueForEntity(id);
}
}
Finally, here's a passing version of the posted test (I had XUnit rather than NUnit handy so changed the assertion and test attribute accordingly):
[Fact]
public void Example() {
var realRepository = new RealRepository();
var proxyRepository = Substitute.For<IRepository>();
proxyRepository
.When(repo => repo.InsertValueForEntity(Arg.Any<int>(), Arg.Any<ValueForEntity>()))
.Do(callInfo => realRepository
.InsertValueForEntity((int)callInfo.Args()[0], (ValueForEntity)callInfo.Args()[1]));
proxyRepository
.GetValueForEntity(Arg.Any<int>())
.Returns(callInfo => realRepository
.GetValueForEntity((int)callInfo.Args()[0]));
var factory = new EntityFactory(proxyRepository);
var entity = factory.CreateEntity(42 /* args */);
proxyRepository.Received().InsertValueForEntity(entity.Id, Arg.Any<ValueForEntity>());
proxyRepository.Received().GetValueForEntity(Arg.Is(entity.Id));
Assert.Equal(42, entity.Id);
}
I know the types don't exactly match, but hopefully you can use this working example to find out the main difference that is causing problems in your fixture.
As an aside, is it worth using a substitute at all here if you could just use realRepository?
I have a method CreateAccount to test. I am using Moq for the same.
Under CreateAccount method, there are multiple table insertion methods which belongs to two classes AccountRepository and BillingRepository
I have setup the Moq but don't know how to use multiple moq objects.
Below is some code snippet
Mock<AccountRepository> moq = new Mock<AccountRepository>();
Mock<BillingRepository> moqBill = new Mock<BillingRepository>();
moq.Setup(x => x.AddTable_1(new AddTable_1 { }));
moq.Setup(x => x.AddTable_2(new AddTable_2 { }));
moqBill.Setup(x => x.Table_3());
CreateAccount method takes four parameters and its under ApplicationService class
public class ApplicationService
{
public CreateAccountServiceResponse CreateAccount(AuthenticateApp App, CustomerInfo Customer, ServiceInfo Service, Optional op)
{
// SOME VALIDATION CODE
//.....................
// SOME CODE TO SAVE DATA INTO TABLES
obj_1.AddTable_1(objdata_1);
obj_1.AddTable_2(objdata_2);
obj_2.AddTable_3(objdata_3);
}
}
Please suggest some solution. How can these three methods will be skipped ?
Thanks in advance.
You have to provide some means to inject obj_1 and obj_2, since they seem to represent your instances of AccountRepository and BillingRepository, resp.
Typically, you might want to do this by using constructor injection. Extending the snippet you provided, this might look like this:
public class ApplicationService
{
private readonly AccountRepository _accountRepository;
private readonly BillingRepository _billingRepository;
public ApplicationService(AccountRepository accountRepository, BillingRepository billingRepository)
{
_accountRepository = accountRepository;
_billingRepository = billingRepository;
}
public CreateAccountServiceResponse CreateAccount(AuthenticateApp App, CustomerInfo Customer, ServiceInfo Service, Optional op)
{
// SOME VALIDATION CODE
//.....................
// SOME CODE TO SAVE DATA INTO TABLES
_accountRepository.AddTable_1(objdata_1);
_accountRepository.AddTable_2(objdata_2);
_billingRepository.AddTable_3(objdata_3);
}
}
Now you can inject your mocks into the class under test:
public void CreateAccount_WhenCalledLikeThis_DoesSomeCoolStuff()
{
var accountRepoMock = new Mock<AccountRepository>();
// set it up
var billingRepository = new Mock<BillingRepository>();
// set it up
var appService = new ApplicationService(accountRepoMock.Object, billingRepoMock.Objcet);
// More setup
// Act
var response = appService.CreateAccount(...);
// Assert on response and/or verify mocks
}
I'm a beginner at writing unit tests and I have a test I'm trying to get working. I'll start of by explaining what I'm trying to test.
I'm trying to test a method which saves messages in a Mvc 4 project. The method is called SaveMessage and is shown below.
namespace ChatProj.Service_Layer
{
public class UserService : IUserService
{
public MessageContext messageContext = new MessageContext();
public UserService()
{
_messageRepository = new MessageRepository(messageContext);
}
private IMessageRepository _messageRepository;
-> public void SaveMessage(Message message)
{
messageContext.Messages.Add(message);
_messageRepository.Save();
}
The _messageRepository.Save in the SaveMessage method is implemented in my DAL layer MessageRepository and looks like this:
public void Save()
{
context.SaveChanges();
}
This way of saving will seem a bit overcomplicated, but I structured the project this way because I didn't want the service layer (IUserService & UserService) to handle operations that could & should (i think) be handled by the Data Access Layer (IMessageRepository & MessageRepository).
Now comes the tricky part. I've been trying to understand how I could unit test this. This is my try:
namespace ChatProj.Tests
{
[TestFixture]
class MessageRepositoryTests
{
[SetUp]
public void Setup()
{
}
[Test]
public void SaveMessage_SaveWorking_VerifyUse()
{
//Arrange
var userServiceMock = new Mock<UserService>();
var message = new Message { MessageID = 0, Name = "Erland", MessageString = "Nunit Test", MessageDate = DateTime.Now };
var repositoryMock = new Mock<IMessageRepository>();
var contextMock = new Mock<MessageContext>();
MessageRepository messageRepository = new MessageRepository(contextMock.Object);
UserService userService = new UserService();
//Act
userService.SaveMessage(message);
//Assert
repositoryMock.Verify(m => m.Save());
userServiceMock.Verify(m => m.SaveMessage(message));
}
}
I get this error: Imgur link , and I'm not quite sure how to solve it. I've tried looking at several other SO posts but I fail to make the test work.
So I'm wondering, how do I practically get my Unit Test to work?
You should setup your MessageContext properties to return fake data and don't make real Db call with SaveChanges method.
Right now it still tries to access a real DB.
But you can setup only virtual properties or if it will be an inteface.
So the best solution is to extract an interface from your MessageContext and inject it into repository. Then you can easily mock your IMessageContext interface and force it to return appropriate in-memory data.
Take a look at these two lines:
UserService userService = new UserService();
//Act
userService.SaveMessage(message);
You're creating a userService instance, and then immediately saving your message. Now jump into the SaveMessage code.
public void SaveMessage(Message message)
{
messageContext.Messages.Add(message);
_messageRepository.Save();
}
Ok, now you're adding stuff to messageContext, and then calling _messageRepository.Save(). But where are messageContext and _messageRepository instantiated?
public MessageContext messageContext = new MessageContext();
public UserService()
{
_messageRepository = new MessageRepository(messageContext);
}
You're creating them at instantiation. The mocks that you've created in your test aren't being used. Instead of creating instances of these objects in the constructor, you might consider passing them into the UserService constructor as arguments. Then, you can pass in mocked instances in your test.
I am trying to get this use case work for me:
I am having a Repository class: ABCRepository
which is having a virtual method GetMyValues()
A Model Class: ABCModel, having a method ABCToTest().
Inside this ABCToTest(), I am trying to access ABCRepository using Ninject:
var repo = kernel.Get<ABCRepository>();
//further using repository method
var results = repo.GetMyValues();
Now, I am using following code to create Unittest for ABCToTest() and mocking GetMyValues() method:
var kernel = new MoqMockingKernel();
kernel.Bind<ABCRepository>().ToMock();
kernel.Bind<ABCModel>().ToMock();
var abcRepo= kernel.GetMock<ABCRepository>();
abcRepo.Setup(repo => repo.GetMyValues()).Returns("ABC");
//This is a static method I am using to initialize the kernel object, ABCToTest() method is using.
MvcApplication.InitializeInjection(kernel);
var model= kernel.GetMock<ABCModel>().Object;
model.ABCToTest("177737");
When I am trying to DEBUG/Run the Test, I can see, the GetMyValues() method call inside ABCTotTest is not returning "ABC" but NULL.
What's wrong I am doing with this implementation? Any suggestions and pointers are highly appreciated..
Thanks & Regards
sumeet
Looks like you've stumbled across the service locator anti-pattern (http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/)
You should change your ABCModel class so you inject an ABCRepository into the constructor:
private readonly IABCRepository repo;
public ABCModel(IABCRepository repo)
{
this.repo = repo;
}
Then your unit test will look like this:
private Mock<IABCRepository> mockRepo;
private ABCModel model;
[SetUp]
public void SetUp()
{
mockRepo = new Mock<IABCRepository>();
model = new ABCModel(mockRepo.Object);
}
[Test]
public void ABCToTest_WhenCalled_CallsRepository
{
model.ABCToTest("177737");
mockRepo.Verify(a => a.GetMyValues(), Times.Once);
}
[unit testing newbie] [c#]
Consider the following scenario:
I'm using Silverlight and calling a WCF service. Silverlight can only call WCF services asynchronously. I build a wrapper around the WCF service so that I can work with Action parameters. (makes the client code a lot cleaner).
So I have an async service that retrieves meeting rooms.
public interface IMeetingRoomService
{
void GetRooms(Action<List<MeetingRoom>> result);
}
Turning GetRooms into List<MeetingRoom> GetRooms() is not an option.
I want to use this service in a ViewModel to set a public property called Rooms.
public class SomeViewModel
{
private readonly IMeetingRoomService _meetingRoomService;
public List<MeetingRoom> Rooms { get; set; }
public SomeViewModel(IMeetingRoomService meetingRoomService)
{
this._meetingRoomService = meetingRoomService;
}
public void GetRooms()
{
// Code that calls the service and sets this.Rooms
_meetingRoomService.GetRooms(result => Rooms = result);
}
}
I want to unit test the implementation of SomeViewModel.GetRooms().
(For this question I quickly wrote the implementation but I'm actually trying to use TDD.)
How do I finish this test?
I'm using NUnit and Moq.
[Test]
public void GetRooms_ShouldSetRooms()
{
var theRooms = new List<MeetingRoom>
{
new MeetingRoom(1, "some room"),
new MeetingRoom(2, "some other room"),
};
var meetingRoomService = new Mock<IMeetingRoomService>();
//How do I setup meetingRoomService so that it gives theRooms in the Action??
var viewModel = new SomeViewModel(meetingRoomService.Object);
viewModel.GetRooms();
Assert.AreEqual(theRooms, viewModel .Rooms);
}
EDIT:
Solution
Read Stephane's answer first.
This is the Test code I ended up writing thanks to stephane's answer:
[Test]
public void GetRooms_ShouldSetRooms()
{
var meetingRoomService = new Mock<IMeetingRoomService>();
var shell = new ShellViewModel(meetingRoomService.Object);
var theRooms = new List<MeetingRoom>
{
new MeetingRoom(1, "some room"),
new MeetingRoom(2, "some other room"),
};
meetingRoomService
.Setup(service => service.GetRooms(It.IsAny<Action<List<MeetingRoom>>>()))
.Callback((Action<List<MeetingRoom>> action) => action(theRooms));
shell.GetRooms();
Assert.AreEqual(theRooms, shell.Rooms);
}
Here is some pseudo code, I haven't run it. But I think that's what you want.
SetupCallback is what you are interested in.
For all the calls to _meetingRoomServiceFake.GetRooms, simply set the _getRoomsCallback to the parameter passed in.
You now have a reference to the callback that you are passing in your viewmodel, and you can call it with whatever list of MeetingRooms you want to test it.
So you can test your asynchronous code almost the same way as synchronous code. it's just a bit more ceremony to setup the fake.
Action<List<MeetingRoom>> _getRoomsCallback = null;
IMeetingRoomService _meetingRoomServiceFake;
private void SetupCallback()
{
Mock.Get(_meetingRoomServiceFake)
.Setup(f => f.GetRooms(It.IsAny<Action<List<MeetingRoom>>>()))
.Callback((Action<List<MeetingRoom>> cb) => _getRoomsCallback= cb);
}
[Setup]
public void Setup()
{
_meetingRoomServiceFake = Mock.Of<IMeetingRoomService>();
SetupCallback();
}
[Test]
public void Test()
{
var viewModel = new SomeViewModel(_meetingRoomServiceFake)
//in there the mock gets called and sets the _getRoomsCallback field.
viewModel.GetRooms();
var theRooms = new List<MeetingRoom>
{
new MeetingRoom(1, "some room"),
new MeetingRoom(2, "some other room"),
};
//this will call whatever was passed as callback in your viewModel.
_getRoomsCallback(theRooms);
}
You could use an AutoResetEvent to handle async calls.
Just initialize it as unset and configure your mock service to set it in the callback.
(IE:
var mockService = new Mock();
mockService.SetUp(x => x.MyMethod()).Returns(someStuff).Callback(() => handle.Set());
)
After that I use hadle.WaitOne(1000) to check if it was called. (IMHO 1000 miliseconds are more than enough to run the async code).
Sorry: This was supposed to go as a reply to the post above... I can't for the life of me figure out how to reply :)