Mocked DbSet method throws NotImplementedException when called inside of controller - c#

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);
}

Related

XUnit before all (fixture) doesn't work for me

I'm trying to fill the In-Memory database with 3 data objects. I'm using a fixture for that purpose.
My Fixture :
public class UBSeedDataFixture : IDisposable
{
public AzDbContext DBContext { get; private set; }
public UBSeedDataFixture()
{
var options = new DbContextOptionsBuilder<AzDbContext>()
.UseInMemoryDatabase(databaseName: "InMemoryTestDb")
.Options;
DBContext = new AzDbContext(options);
DBContext.UBs.Add(new UB { Title = "UB1", Code = "SomeCode" });
DBContext.UBs.Add(new UB { Title = "UB2", Code = "SomeCode" });
DBContext.UBs.Add(new UB { Title = "UB3", Code = "SomeCode" });
DBContext.SaveChanges();
}
public void Dispose()
{
DBContext.Database.EnsureDeleted();
DBContext.Dispose();
}
}
then I run two simple tests:
public class UBTests : IClassFixture<UBSeedDataFixture>
{
UBSeedDataFixture fixture;
public UBTests(UBSeedDataFixture fixture)
{
this.fixture = fixture;
}
[Fact]
public void DeleteTest()
{
var result = fixture.DBContext.UBs.SingleOrDefault(u => u.Title == "UB1");
fixture.DBContext.Remove(result);
fixture.DBContext.SaveChanges();
result = fixture.DBContext.UBs.SingleOrDefault(u => u.Title == "UB1");
Assert.Equal(null, result);
}
[Fact]
public void AddTest()
{
var ubs = fixture.DBContext.UBs.ToList();
Assert.Equal("UB1", ubs[0].Title);
}
}
As the result, I get an error that I get more than one result when I query data with Title == "UB1". If I debug my tests I have 6 elements in my database, when my fixture is supposed to create only 3. It seems like my fixture runs every time when a new test runs. How can I fix it so I have a fixture something like "Once before all tests"?

Moq Setup dont working, while trying to automate it

Main Goal : Automatic Moq an entire DBContext
Current Problem: Setup donĀ“t work. It runs and adds the Setup but when you try to access the Data you get an "NotImplemented Exception"
The member 'IQueryable.Provider' has not been implemented on type 'DbSet 1Proxy_4' which inherits from 'DbSet 1'. Test doubles for 'DbSet 1' must provide implementations of methods and properties that are used.
My class
public abstract class MoqBase<T> where T : DbContext
{
public Mock<T> MockContext { get; private set; }
public void Mock()
{
MockContext = new Mock<T> { DefaultValue = DefaultValue.Mock };
MockContext.SetupAllProperties();
PrepareContext();
}
private void PrepareContext()
{
var propertyList = MockContext.Object.GetType().GetProperties();
var tablePropertyList = propertyList.Where(x => x.PropertyType.FullName.Contains("DbSet")).ToList();
foreach (var tableProperty in tablePropertyList)
{
var proxy = tableProperty.GetValue(MockContext.Object);
dynamic mockSet = ((dynamic)proxy).Mock;
AddTableSetup(mockSet);
}
}
public void AddTableSetup<TTable>(Mock<DbSet<TTable>> Mockset) where TTable : class
{
var list = new List<TTable>();
var data = list.AsQueryable();
Mockset.As<IQueryable<TTable>>().Setup(m => m.Provider).Returns(data.Provider);
Mockset.As<IQueryable<TTable>>().Setup(m => m.Expression).Returns(data.Expression);
Mockset.As<IQueryable<TTable>>().Setup(m => m.ElementType).Returns(data.ElementType);
Mockset.As<IQueryable<TTable>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator);
}
}
TestMoq which implements MoqBase
public class TestMoq : MoqBase<NetConPortalContext>//NetConPortalContext is my dbContext
{
public TestMoq() => Mock();
}
Try to access
static void Main()
{
var moq = new TestMoq();
var context = moq.MockContext.Object;
var test = context.User.FirstOrDefault();
}
Picture of Error and Setup in Debug

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.

Preventing Autofixture from filling child collections

I'm using the latest version of Autofixture, and I'd like to prevent it from filling automatically child collections.
For example, I have a class Person that has a List property. I want all properties filled, except the list.
I tried using this customization :
public class RemoveMultiples : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations
.OfType<FilteringSpecimenBuilder>()
.Where(x => x.Specification is DictionarySpecification)
.ToList().ForEach(c => fixture.Customizations.Remove(c));
fixture.Customizations
.OfType<FilteringSpecimenBuilder>()
.Where(x => x.Specification is CollectionSpecification)
.ToList().ForEach(c => fixture.Customizations.Remove(c));
fixture.Customizations
.OfType<FilteringSpecimenBuilder>()
.Where(x => x.Specification is HashSetSpecification)
.ToList().ForEach(c => fixture.Customizations.Remove(c));
fixture.Customizations
.OfType<FilteringSpecimenBuilder>()
.Where(x => x.Specification is ListSpecification)
.ToList().ForEach(c => fixture.Customizations.Remove(c));
}
}
But it also prevents me from using .CreateMany().
edit: I can use .CreateMany(3) and it works.
Is there a setting somewhere that could let me autofill collections only when I need to?
edit2: Class person should look like this:
[Serializable]
public class Person
{
private ICollection<OtherClass> _otherClasses;
private string _something;
public virtual ICollection<OtherClass> OtherClasses
{
get { return _otherClasses; }
set { _otherClasses = value; }
}
}
Note that it's not always a Collection, but sometimes IList
Note2: I just realized that someone also removed the Customization for IEnumerable hence why the CreateMany() doesn't create anything.
Here's one way to do it.
Start by implementing a SpecimenBuilder that tells AutoFixture to skip assigning a value for collection property:
public class CollectionPropertyOmitter : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
var pi = request as PropertyInfo;
if (pi != null
&& pi.PropertyType.IsGenericType
&& pi.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>))
return new OmitSpecimen();
return new NoSpecimen(request);
}
}
Then encapsulate that in a Customization:
public class DoNotFillCollectionProperties : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(new CollectionPropertyOmitter());
}
}
The following tests now pass:
[Fact]
public void CreatePersonWithoutFillingCollectionProperty()
{
var fixture = new Fixture().Customize(new DoNotFillCollectionProperties());
var actual = fixture.Create<Person>();
Assert.Null(actual.OtherClasses);
}
[Fact]
public void CreateManyStillWorks()
{
var fixture = new Fixture().Customize(new DoNotFillCollectionProperties());
var actual = fixture.CreateMany<Person>();
Assert.NotEmpty(actual);
}
[Fact]
public void CreatListStillWorks()
{
var fixture = new Fixture().Customize(new DoNotFillCollectionProperties());
var actual = fixture.Create<List<Person>>();
Assert.NotEmpty(actual);
}
[Fact]
public void CreateCollectionStillWorks()
{
var fixture = new Fixture().Customize(new DoNotFillCollectionProperties());
var actual = fixture.Create<ICollection<Person>>();
Assert.NotEmpty(actual);
}

How to unit test Create POST?

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());
}

Categories

Resources