What's the best practice if I'd like to unit test an entity with a referenced property?
BlogEntry is referencing a User object by a foreign key. Right now I'm using session.Load to avoid an exception since the foreign key cannot contain nulls.
Can i Mock this somehow instead? I don't want my unit test to contain references to a "real" user in the db.
public class BlogEntry {
public virtual int ID {get;set;}
public virtual User CreatedBy {get;set;}
public virtual string Text {get;set;}
}
I'm currently using the following test method:
[Test]
public void Create_blog_entry()
{
using (var session = sessionFactory.OpenSession())
using (var trans = session.BeginTransaction())
{
var entry = new BlogEntry(){
Text = "Lorem ipsum",
CreatedBy = session.Load<User>(1)
};
session.Save(entry);
trans.Rollback();
}
}
[Test]
public void Create_blog_entry()
{
using (var session = sessionFactory.OpenSession())
using (var trans = session.BeginTransaction())
{
var user = new User { Id = 1 };
session.Save(user);
var entry = new BlogEntry()
{
Text = "Lorem ipsum",
CreatedBy = user
};
session.Save(entry);
trans.Rollback();
}
}
Related
I have the following Unit Test:
[Fact]
public void FetchStudents_Rendered_Test()
{
var testData = new List<Student>()
{
new Student()
{
Id = 1,
Name = "Sample Name",
Email = "sample#email.com",
Phone = "123456789",
Address = "Sample address"
}
};
var mockDbSet = Mock.Of<DbSet<Student>>(dbSet => dbSet.AsQueryable() == testData.AsQueryable());
DbContextOptions<ApplicationDbContext> options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName: "StudentsTest")
.Options;
var mockDbContext = new Mock<ApplicationDbContext>(options);
using var ctx = new TestContext();
ctx.Services.AddSingleton<IStudentsService>(new StudentsService(mockDbContext.Object));
// RenderComponent will inject the service in the WeatherForecasts component
// when it is instantiated and rendered.
var cut = ctx.RenderComponent<FetchStudents>();
// Assert that service is injected
Assert.NotNull(cut.Instance.students);
}
The StudentService looks like below:
public class StudentsService : IStudentsService
{
private readonly ApplicationDbContext _db;
public StudentsService(ApplicationDbContext db)
{
_db = db;
}
public async Task<List<Student>> GetStudentsAsync()
{
return await _db.Students.ToListAsync();
}
}
When I run the unit test I get the following error:
System.InvalidOperationException: 'The source 'IQueryable' doesn't implement 'IAsyncEnumerable<BlazorStudentApp.Data.Models.Student>'. Only sources that implement 'IAsyncEnumerable' can be used for Entity Framework asynchronous operations.'
I have tried setting up the DbSet in multiple different ways, but I keep getting the same error.
What am I missing here?
I have a custom TagHelper which extends the OOTB InputTagHelper. I am conditionally adding attributes to it based on the presence of a custom ValidationAttribute on the model property associated with it. The code for this in the TagHelper .Process method works fine:
if (this.For.Metadata.ValidatorMetadata.OfType<NumericValidationAttribute>().Any())
{
output?.Attributes.SetAttribute(new TagHelperAttribute("inputmode", "numeric"));
output?.Attributes.SetAttribute(new TagHelperAttribute("pattern", "[0-9]*"));
}
My issue is in Unit Testing this. I have other Unit Tests written using the code available in the Net Core MVC Test repo: https://github.com/aspnet/Mvc/blob/master/test/Microsoft.AspNetCore.Mvc.TagHelpers.Test/
... but there's no real steer on how to create the For i.e ModelExpression for the property I want to test which has this Validation attribute associated with it: e.g.
public class TestModel
{
[NumericValidation(ErrorMessage = "Error message")]
public string Field1 { get; set; }
}
I want to be able to populate the For.Metadata.ValidatorMetadata list for this ModelExpression and I don't know how.
The full Unit Test which doesnt work is:
[Fact]
public void CustomInputTagHelperProcess_NumericValidationAttributeOnModelProperty_GeneratesCorrectHtml()
{
// Arrange
var metadataProvider = new EmptyModelMetadataProvider();
var htmlGenerator = new TestableHtmlGenerator(metadataProvider);
var model = new TestModel
{
Field1 = "cc",
};
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(TestModel), model);
var modelExpression = new ModelExpression(name: "Field1", modelExplorer: modelExplorer);
var viewContext = TestableHtmlGenerator.GetViewContext(model, metadataProvider);
var attributes = new TagHelperAttributeList
{
{ "name", PropertyName },
{ "type", InputTypeName },
};
var tagHelperContext = new TagHelperContext(attributes, new Dictionary<object, object>(), nameof(CustomInputTagHelperTest));
var output = new TagHelperOutput(
Input,
new TagHelperAttributeList(),
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(result: null))
{
TagMode = TagMode.SelfClosing,
};
var customInputTagHelper = new CustomInputTagHelper(this.htmlGenerator)
{
For = this.modelExpression,
InputTypeName = InputTypeName,
Name = PropertyName,
ViewContext = this.viewContext,
};
// Act
customInputTagHelper.Process(this.tagHelperContext, output);
// Assert - ensure we have an inputmode attribute on the input.
Assert.Contains(output.Attributes, x => x.Name == "inputmode" && x.Value.ToString() == "numeric");
}
Any thoughts?
I am trying to unit test my service layer as advised by #NKosi Here. I am able to do the integration test successfully by implementing the actual factory implementation without mocking anything but can't do the unit test (by mocking IDbConnection and my SQL connection factory class) as Dapper query executing fails with the error 'Object not set to an instance of an object'.
My IDbConnection factory and its implementation is as follow
public interface IDbConnectionFactory
{
IDbConnection CreateConnection();
}
public class ConnectionSetings
{
public string Name { get; set; }
}
public class SqlConnectionFactory : IDbConnectionFactory
{
private readonly ConnectionSetings connectionSettings;
public SqlConnectionFactory(ConnectionSetings connectionSettings)
{
this.connectionSettings = connectionSettings;
}
public IDbConnection CreateConnection()
{
return new SqlConnection(connectionSettings.Name);
}
}
And the XUnit test is as follow
[Fact]
public void Get_RestaurantById_ReturnsRestaurant()
{
//Arrange
var connection = new Mock<IDbConnection>();
var dbConnectionFactory = new Mock<IDbConnectionFactory>();
dbConnectionFactory.Setup(x => x.CreateConnection()).Returns(connection.Object);
//Act
var result = new SqlRestaurantDataCL(dbConnectionFactory.Object).Get(1);
//Assert
result.Name.Equals("Test Name 1");
//Assert.Equal("Test Name 1", result.Name);
}
And the Service Layer is as follow
public class SqlRestaurantDataCL : IRestaurantDataCL
{
private readonly IDbConnectionFactory factory;
public SqlRestaurantDataCL(IDbConnectionFactory factory)
{
this.factory = factory;
}
public Restaurant Get(int id)
{
using (var connection = factory.CreateConnection())
{
var selectSql = #"SELECT * From Restaurants Where Id = #Id";
var restaurant = connection.QuerySingleOrDefault<Restaurant>(selectSql, new
{
id
});
return restaurant;
}
}
}
Following is the error screenshot
Following is the answer to my question if anyone is in similar situation. Before following this solution, I would suggest to read #NKosi comments above and consult #Mikhail's solution Here.
ServiceStack.OrmLite.Sqlite package added to use in memory appraoch
internal class InMemoryDatabase
{
private readonly OrmLiteConnectionFactory dbFactory = new OrmLiteConnectionFactory(":memory:", SqliteDialect.Provider);
public IDbConnection OpenConnection() => this.dbFactory.OpenDbConnection();
public void Insert<T>(IEnumerable<T> items)
{
using (var db = this.OpenConnection())
{
db.CreateTableIfNotExists<T>();
foreach (var item in items)
{
db.Insert(item);
}
}
}
}
Data Access layer is as follow
public IEnumerable<Restaurant> GetAll()
{
using (var connection = factory.CreateConnection())
{
//return connection.Query<Restaurant>("Select * From [dbo].[Restaurants] Order By Name");
return connection.Query<Restaurant>("Select * From Restaurant Order By Name");
}
}
Unit test is as follow
[Fact]
public void Get_RestaurantById_ReturnsRestaurant()
{
//Arrange
var restaurants = new List<Restaurant>
{
new Restaurant { Id = 1, Name = "Test Name 1", Cuisine = CuisineType.None},
new Restaurant { Id = 2, Name = "Test Name 2", Cuisine = CuisineType.French},
new Restaurant { Id = 3, Name = "Test Name 3", Cuisine = CuisineType.German},
new Restaurant { Id = 4, Name = "Test Name 4", Cuisine = CuisineType.Italian},
new Restaurant { Id = 5, Name = "Test Name 5", Cuisine = CuisineType.None}
};
var db = new InMemoryDatabase();
db.Insert(restaurants);
var connection = new Mock<IDbConnection>();
var dbConnectionFactoryMock = new Mock<IDbConnectionFactory>();
dbConnectionFactoryMock.Setup(c => c.CreateConnection()).Returns(db.OpenConnection());
//Act
var result = new SqlRestaurantDataCL(dbConnectionFactoryMock.Object).GetAll();
//Assert
result.Should().BeEquivalentTo(restaurants);
}
I have 2 simple tables.
create table Owner
(
Id int primary key,
Name nvarchar(100),
);
create table Status
(
Id int primary key,
BrandName nvarchar(50)
OwnerId int foreign key references Owner(Id),
);
In app I map these tables to model classes:
public class Owner
{
public int Id {get;set;}
public string Name{get;set;}
public Status Status {get;set;}
}
public class Status
{
public int Id {get;set;}
public string Brand {get;set;}
public int OwnerId {get;set;}
}
I use dapper and dapper extension.
I would like map relation one to one in dapper in classmapper. It’s possible?
My goal is when I added owner object which has set up also property Status to db via repository
it also insert record do status table.
What is best way to achieve this behavior?
public class OwnerMapper : ClassMapper<Owner>
{
public OwnerMapper()
{
Table("Owner");
Map(p=>p.Id).Column("Id").Key(KeyType.Assigned);
Map(p=>p.Name).Column("Name");
//how map property status
}
}
public class StatusMapper : ClassMapper<Status>
{
public StatusMapper()
{
Table("Status");
Map(p=>p.Id).Column("Id").Key(KeyType.Identity);
Map(p=>p.Brand).Column("BrandName");
Map(p=>OwnerId).Column("OwnerId");
}
}
You can try Dapper's multi-mapping feature:
[Test]
public void MyTest()
{
var connection = new SqlConnection("conn string here");
connection.Open();
const string sql =
"select Id = 1, Name ='Bill Gates', Id = 1, Brand = 'Apple', OwnerId = 1";
var result = connection.Query<Owner, Status, Owner>(sql,
(owner, status) =>
{
owner.Status = status;
return owner;
},
commandType: CommandType.Text
).FirstOrDefault();
Assert.That(result, Is.Not.Null);
Assert.That(result.Status, Is.Not.Null);
Assert.That(result.Status.Brand, Is.EqualTo("Apple"));
connection.Close();
}
I have this controller:
[HttpPost]
public ActionResult Create(Company company)
{
// try to save the record
if (ModelState.IsValid)
{
// create the command
var command = new CreateOrUpdateCompanyCommand
{
CompanyId = company.CompanyId,
Code = company.Code,
Name = company.Name
};
// check for errors
IEnumerable<ValidationResult> errors = _commandBus.Validate(command);
ModelState.AddModelErrors(errors);
if (ModelState.IsValid)
{
var result = _commandBus.Submit(command);
if (result.Success)
return RedirectToAction("Index");
}
}
// if fail
return View("Create", company);
}
I have this test for NUnit:
[Test]
public void Create()
{
const string expectedRouteName = "Index";
// Mock the arguments
var mockRepository = Substitute.For<ICompanyRepository>();
var mockProcessor = Substitute.For<ICommandBus>();
// Arrange
var controller = new CompanyController(mockProcessor, mockRepository);
// Act
var company = new Company
{
Code = "XXXXXXX",
CompanyId = 1,
Name = "Sample company"
};
var result = controller.Create(company) as RedirectToRouteResult;
// Assert
Assert.IsNotNull(result, "Should return a ViewResult");
Assert.AreEqual(expectedRouteName, result.RouteValues["action"],
"View name should have been '{0}'", expectedRouteName);
}
This is the Model:
public class Company
{
[Key]
public int CompanyId { get; set; }
[Required(ErrorMessage = "* (xxxx)")]
[MaxLength(7)]
[RegularExpression("^([A-Z0-9]{7})$", ErrorMessage = "xxx")]
[Display(Name = "Code")]
public string Code { get; set; }
[Required(ErrorMessage = "*")]
[MaxLength(40)]
[Display(Name = "Name")]
public string Name { get; set; }
}
When I Run the test, this function always return false: var result = _commandBus.Submit(command); and the test fails.
I don't know how to test it? Should I mock the _commandBus and set it to return true? I tried in this way but unsuccessfully:
var mockCommand = Substitute.For<ICommand>();
mockProcessor.Submit(mockCommand).Success.Returns(true);
To create this project I have got inspiration from this http://efmvc.codeplex.com/
Any advise to me? Thanks.
The command you pass to mockProcessor.Submit(mockCommand).Success.Returns(true) needs to be the same one that the code-under-test passes. As the method news up its own command this will never be the case.
Easiest fix is to match any command when you set up your substitute:
var result = CreateSuccessfulResult(); // <-- fill this in as appropriate
mockProcessor.Submit(null).ReturnsForAnyArgs(result);
Setting the Success field on result inline as per your original test should work too I think, but I find it a bit clearer to specify the result required.
You can improve this a bit by matching the expected command type:
mockProcessor.Submit(Arg.Any<CreateOrUpdateCompanyCommand>()).Returns(result);
You can also inspect the command passed if you'd like to test that:
mockProcessor
.Submit(Arg.Is<CreateOrUpdateCompanyCommand>(x => x.CompanyId = ...))
.Returns(result);
A similar approach can be used to check the Validate call.
There's a bit more info in the NSubstitute docs.