How to unit test Create POST? - c#

How do I unit test that the journal entry was created correctly (added to the db with the correct values)?
[HttpPost]
public ActionResult Create(
int journalId,
string text)
{
JournalEntry journalentry = new JournalEntry();
journalentry.Text = text;
if (ModelState.IsValid)
{
var journal = db.Journals.Find(journalId);
journalentry.Journal = journal;
db.JournalEntries.Add(journalentry);
db.SaveChanges();
}
return View(journalentry);
}

You should not unit test against a real DbContext instance.
Your unit tests should instead mockup interfaces and inject them into the controller.
For example:
public class JournalsController : Controller
{
private readonly IJournalsRespository _journalsDb;
public JournalsController(IJournalsRepository journalsDb)
{
_journalsDb = journalsDb
}
[HttpPost]
public ActionResult Create(int journalId, string text)
{
JournalEntry journalentry = new JournalEntry();
journalentry.Text = text;
if (ModelState.IsValid)
{
var journal = _journalsDb.FindJournal(journalId);
journalentry.Journal = journal;
_journalsDb.Add(journalentry);
_journalsDb.SaveChanges();
}
return View(journalentry);
}
}
Your interface would look something like this:
public interface IJournalsRepository
{
Journal FindJournal(int id);
void Add(JournalEntry journalEntry);
void SaveChanges();
}
You could unit test this like so (using Moq):
public void Create()
{
var db = new Mock<IJournalsRepository>();
db.Setup(m => m.FindJournal(7)).Returns(new Journal());
var controller = new JournalsController(db.Object);
controller.Create(7, "test");
db.Verify(m => m.FindJournal(7), Times.Once());
db.Verify(m => m.Add(It.IsAny<JournalEntry>()), Times.Once());
db.Verify(m => m.SaveChanges(), Times.Once());
}

Related

Record odd behavior in Unit Tests

I've written unit tests using NSubstitute library and faced with odd Records behavior.
Assuming I have the code:
public record SomeModel
{
//fields
}
public interface ISomeService
{
public string DoSmth(SomeModel model);
}
public class SomeClass
{
private ISomeService _service;
public SomeClass(ISomeService service)
{
_service = service;
}
public string MethodToTest(SomeModel model)
{
return _service.DoSmth(model);
}
}
And I want to cover it with the Unit test:
[Fact]
public void Test()
{
var model = Substitute.For<SomeModel>();
var service = Substitute.For<ISomeService>();
service.DoSmth(model).Returns("1234");
var sut = new SomeClass(service);
var value = sut.MethodToTest(model); //it returns Empty sting here
value.Should().Be("1234");
}
But it Fails, because MethodToTest returns Empty string.
If I change SomeModel from record to class it is working fine.
Could someone point me to what I missed here?
Update: I changed the model initialization in my test method:
[Fact]
public void Test()
{
var model = new SomeModel();
//other code
}
In this case, it is working. But I still don't understand why it isn't working in the initial example?
Update 2. It is even working if I use Substitute.ForPartsOf:
[Fact]
public void Test()
{
var model = Substitute.ForPartsOf<SomeModel>();
//other code
}

Test method still calls the actual method despite mock

So I have some initial setup that looks like this:
ILoginManager _loginManager;
Mock<IValidations> _validations;
Mock<IAccountRepository> _accountRepository;
[SetUp]
public void Setup()
{
_loginManager = new LoginManager();
_validations = new Mock<IValidations>();
_accountRepository = new Mock<IAccountRepository>();
}
Then I have the test method that looks like this:
[Test]
public void Login_ValidUser()
{
_validations.Setup(val => val.IsValidAccount(It.IsAny<User>())).Returns(true);
_accountRepository.Setup(repo => repo.Login(It.IsAny<User>())).Returns(()=>new User());
var result = _loginManager.Login(new User());
Assert.That(result, Is.Not.Null);
}
And the actual method that looks like this:
public User Login(User user)
{
if (user != null && _validations.IsValidAccount(user))
{
return _accountDal.Login(user);
}
log.Error("User null or not valid");
return null;
}
The problem is that the test method still calls the original methods so it ignores the mock setup.
Those dependencies would have had to have been injected directly into the manager in order for them to be available for mocking when testing
For example
public class LoginManager : ILoginManager {
private readonly IValidations _validations;
private readonly IAccountRepository _accountDal;
public LoginManager(IValidations validations, IAccountRepository accountDal) {
_validations = validations;
_accountDal = accountDal;
}
public User Login(User user) {
if (user != null && _validations.IsValidAccount(user)) {
return _accountDal.Login(user);
}
log.Error("User null or not valid");
return null;
}
}
Your current example is not using the mocks in the class under test because they were not injected.
Refactor the LoginManager class and the test accordingly
ILoginManager _loginManager;
Mock<IValidations> _validations;
Mock<IAccountRepository> _accountRepository;
[SetUp]
public void Setup() {
_validations = new Mock<IValidations>();
_accountRepository = new Mock<IAccountRepository>();
_loginManager = new LoginManager(_validations.Object, _accountRepository.Object);
}
[Test]
public void Login_ValidUser() {
//Arrange
var expected = new User();
_validations.Setup(val => val.IsValidAccount(It.IsAny<User>())).Returns(true);
_accountRepository.Setup(repo => repo.Login(It.IsAny<User>())).Returns(()=> user);
//Act
var actual = _loginManager.Login(expected);
//Assert
Assert.That(actual, Is.Not.Null);
Assert.AreEqual(expected, actual);
}

Mocked DbSet method throws NotImplementedException when called inside of controller

I have the following setup:
DbContext:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public virtual DbSet<Album> Album { get; set; }
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
Model:
public class Album
{
public int AlbumID { get; set; }
[StringLength(150)]
public string Title { get; set; }
}
Controller:
public class AlbumController : Controller
{
ApplicationDbContext db = new ApplicationDbContext();
public AlbumController(ApplicationDbContext injectDb)
{
db = injectDb;
}
// POST: Albums/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Admin")]
public ActionResult DeleteConfirmed(int id)
{
Album album = db.Album.Find(id);
db.Album.Remove(album);
db.SaveChanges();
return RedirectToAction("Index");
}
}
I wrote the unit test using Moq and xUnit to check DeleteConfirmed functionality:
public class AlbumsControllerTests
{
public static Mock<DbSet<T>> MockDbSet<T>(List<T> inputDbSetContent) where T : class
{
var DbSetContent = inputDbSetContent.AsQueryable();
var dbSet = new Mock<DbSet<T>>();
dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(DbSetContent.Provider);
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(DbSetContent.Expression);
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(DbSetContent.ElementType);
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => inputDbSetContent.GetEnumerator());
dbSet.Setup(m => m.Add(It.IsAny<T>())).Callback<T>((s) => inputDbSetContent.Add(s));
dbSet.Setup(m => m.Remove(It.IsAny<T>())).Callback<T>((s) => inputDbSetContent.Remove(s));
return dbSet;
}
[Fact]
public void DeleteConfirmedTest()
{
// Arrange
var mockAlbumSet = MockDbSet(new List<Album> { });
Mock<ApplicationDbContext> sutDbContext = new Mock<ApplicationDbContext>() { CallBase = true };
sutDbContext.Setup(m => m.Album).Returns(mockAlbumSet.Object);
// Check if Album.Remove works inside this test
var albumToBeDeleted = new Album() { AlbumID = 1, Title = "TestAlbumName" };
sutDbContext.Object.Album.Add(albumToBeDeleted);
Assert.Equal(1, (from a in sutDbContext.Object.Album select a).Count());
sutDbContext.Object.Album.Remove(albumToBeDeleted);
Assert.Equal(0, (from a in sutDbContext.Object.Album select a).Count());
// Actual Test
sutDbContext.Object.Album.Add(albumToBeDeleted);
sutDbContext.Setup(m => m.Album.Find(It.IsAny<int>()))
.Returns(albumToBeDeleted);
AlbumController sut = new AlbumController(sutDbContext.Object);
var output = sut.DeleteConfirmed(1); // Throws NotImplementedException
// Assert
Assert.Equal(0, (from a in sutDbContext.Object.Album select a).Count());
}
}
The test throws the following exception on db.Album.Remove(album) in DeleteConfirmed:
System.NotImplementedException : The member 'Remove' has not been
implemented on type 'DbSet1Proxy' which inherits from 'DbSet1'. Test
doubles for 'DbSet`1' must provide implementations of methods and
properties that are used.
As you can see in MockDbSet method body, I setup Remove method for my Mock and it works just fine inside the unit test. Can you explain me why it doesn't work inside the controller?
Your test will work fine if you change your line:
sutDbContext.Setup(m => m.Album.Find(It.IsAny<int>()))
.Returns(albumToBeDeleted);
To:
mockAlbumSet.Setup(x=>x.Find(It.IsAny<int>()))
.Returns(albumToBeDeleted);
You made a setup for your sutDbContext to return mockAlbumSet.Object when sutDbContext.Album is called, but that line overridden your setup to create a new mock object for sutDbContext.Album property and created a single setup for that mock:
m.Album.Find(It.IsAny<int>()))
.Returns(albumToBeDeleted);
Here is a simple test, that shows you that calling setup for nested property of the class, that was previously setup to return a Mock.Object, will override that property with a new Mock.Object:
public interface IParentService
{
IDependantService Dependant { get; }
}
public interface IDependantService
{
void Execute();
}
[Fact]
//This test passes
public void VerifyThatNestedMockSetupGeneratesNewMockObject()
{
var value = 0;
var parentServiceMock = new Mock<IParentService>();
var dependantServiceMock = new Mock<IDependantService>();
dependantServiceMock.Setup(x => x.Execute()).Callback(() => { value = 1; });
parentServiceMock.Setup(x => x.Dependant).Returns(dependantServiceMock.Object);
Assert.Same(parentServiceMock.Object.Dependant, dependantServiceMock.Object);
parentServiceMock.Setup(x => x.Dependant.Execute()).Callback(() => { value = 2; });
Assert.NotSame(parentServiceMock.Object.Dependant, dependantServiceMock.Object);
parentServiceMock.Object.Dependant.Execute();
Assert.Equal(2, value);
}

How to Unit Test without Hitting Database

I want to ask what is a good unit test for the method below GetMeetingsByInterimIdentifier where interim identifier is a string -- such as 78th2015.
We are setup to use the interface IMeetingsService. We are using MOQ and Microsoft.VisualStudio.TestTools.UnitTesting.
public class MeetingsService : IMeetingsService
{
private readonly IInterimCommitteeDbContext _db;
public MeetingsService(IInterimCommitteeDbContext db)
{
this._db = db;
}
public IQueryable<Meeting> GetMeetingsByInterimIdentifier(string interimIdentifier)
{
return
from m in this._db.Meetings
join c in this._db.Committees on m.CommitteeId equals c.CommitteeId
where c.InterimIdentifier == interimIdentifier
select m;
}
public Meeting GetMeeting(int meetingKey)
{
return this._db.Meetings.FirstOrDefault(x => x.MeetingId == meetingKey);
}
}
Edit:
But I am not sure how to set it up. This result is not null, but what does it do for me?
[TestMethod]
public void GetMeetingsByInterimIdentifier_WithInterimIdentifier_ReturnsMeetingList()
{
//Arrange
var interim = Properties.Settings.Default.DefaultInterimIdentifier;
var result = _meetingServiceMock.Setup(x => x.GetMeetingsByInterimIdentifier(interim));
//Act
//Assert
Assert.IsNotNull(result);
}
Create a Mock<IInterimCommitteeDbContext> and pass it into the constructor. On this object setup the Meetings and Committees properties to return various collections.
You should have different tests setup that return different collections. For example, how should this behave if both the Meetings and Committees are empty, i.e. there is no data in the database? How should it behave if there isn't an object with the provided InterimIdentifier? What about if there is one that matches etc.
I figured out how to do this using test doubles. I am using Entity Framework 6 with the code first model. I created a DbContext that inherited from my I-DbContext interface. Then I was able to create in-memory data to use in my service layer unit tests. Below is an example of:
the test data context,
the test dbset,
an example unit test.
This solution was available from an msdn article here:
https://msdn.microsoft.com/en-us/data/dn314429.aspx
...
public class CommitteeContextTest : ICommitteeDbContext
{
public CommitteeContextTest()
{
this.Committees = new TestDbSet();
this.CommitteeMembers = new TestDbSet();
}
public Database Database { get; }
public DbSet Committees { get; set; }
public DbSet CommitteeMembers { get; set; }
}
}
public class TestDbSet : DbSet, IQueryable, IEnumerable, IDbAsyncEnumerable
where TEntity : class
{
ObservableCollection _data;
IQueryable _query;
public TestDbSet()
{
_data = new ObservableCollection();
_query = _data.AsQueryable();
}
public override TEntity Add(TEntity item)
{
_data.Add(item);
return item;
}
public override TEntity Remove(TEntity item)
{
_data.Remove(item);
return item;
}
public override TEntity Attach(TEntity item)
{
_data.Add(item);
return item;
}
public override TEntity Create()
{
return Activator.CreateInstance();
}
}
[TestClass]
public class CommitteeServiceTest
{
private InterimCommitteeContextTest _interimCommitteeContext;
private ICommitteeService _service;
private string _interim;
[TestInitialize]
public void SetUp()
{
_interimCommitteeContext = new InterimCommitteeContextTest();
_service = new CommitteeService(_interimCommitteeContext);
_interim = Settings.Default.DefaultInterimIdentifier;
}
[TestCleanup]
public void Teardown()
{
_interimCommitteeContext = null;
_service = null;
}
[TestMethod]
public void GetCommittee_ProvideInterimCommitteeId_ReturnOneCommittee()
{
//Arrange
AddCommittees();
//Act and Assert
var result = _service.GetCommittee(_interim, 1);
Assert.AreEqual(1, result.CommitteeId); //Passes. IsActive set to true;
result = _service.GetCommittee(_interim, 0);
Assert.IsNull(result); //Fails. No committeeId = 0;
result = _service.GetCommittee(_interim, 2);
Assert.IsNull(result); //Fails. CommitteeId = 2 is not active.
}
[TestMethod]
public void AddCommittees()
{
_interimCommitteeContext.Committees.Add(new Committee() { CommitteeId = 1, InterimIdentifier = _interim, IsActive = true, CommitteeTypeId = 1 });
_interimCommitteeContext.Committees.Add(new Committee() { CommitteeId = 2, InterimIdentifier = _interim, IsActive = false, CommitteeTypeId = 1 });
_interimCommitteeContext.Committees.Add(new Committee() { CommitteeId = 3, InterimIdentifier = _interim, IsActive = true, CommitteeTypeId = 1 });
}
}
Use Mocking, that's what it is for. Use JMock or Mockito or any other library you prefer.

Mock class method inside controller with MOQ

Using the sample code below how could I use MOQ to mock a (referenced dll) class method
[TestMethod]
public void SampleTestMethod()
{
var _controller = new MyController();
var stub = new Mock<class1>();
stub.Setup(x => x.DoSomething(It.IsAny<int>())).Returns(2);
//var retval = stub.Object.DoSomething();
var result = _controller.MyAction() as ViewResult;
stub.Verify(x => x.DoSomething(It.IsAny<int>()), Times.AtLeastOnce(), "Didn't call DoSomething");
}//end test
//Controller
public class MyController{
public MyController(){}
public virtual ActionResult MyAction(){
var ret = new class1();
var result = ret.DoSomething(10);
//rest of code
return View();
}
}
//external class
public class class1
{
public virtual int DoSomething(int i)
{
return 1;
}
}
You will need to inject an instance of the external class 'class1' into your controller's constructor. Then you can use Moq to mock it and inject in the fake one. Something like this:
public class MyController{
private readonly IClass1 _class1;
public MyController(IClass1 class1){
_class1 = class1;
}
// Other code uses this private instance
}
[TestMethod]
public void Test(){
var class1 = new Mock<Class1>();
var controller = new MyController(class1.Object);
}

Categories

Resources