egmfrs egmfrs - 1 year ago 93
C# Question

EF inMemory provider falsely accepting null entries when the fields are set to required

Here is my unit test code:

[Fact(DisplayName = "Should only add AssetType when Name Provided")]
public async Task Test4()
{
using (var context = GetContext()) {

var listAssetTypes = await context.AssetType.ToListAsync();

Assert.Equal(0, listAssetTypes.Count);

var goodAssetType = new AssetType { Name = "1st Item" };
context.Add(goodAssetType);
await context.SaveChangesAsync();
listAssetTypes = await context.AssetType.ToListAsync();

Assert.Equal(1, listAssetTypes.Count);

var badAssetType = new AssetType {};
context.Add(badAssetType);
await context.SaveChangesAsync();
listAssetTypes = await context.AssetType.ToListAsync();

Assert.Equal(1, listAssetTypes.Count);
}
}


The first two assert's pass. The 3rd fails, Actual is 2, and if I debug I can see a new Id has been assigned and the name is set to null.

Here is my context method:

private WorldContext GetContext()
{
var builder = new ConfigurationBuilder();
var config = builder.Build();
var options = new DbContextOptionsBuilder<WorldContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString()).Options;
var context = new WorldContext(config, options);
return context;
}


And my model:

public class AssetType
{
[DatabaseGenerated(databaseGeneratedOption: DatabaseGeneratedOption.Identity)]
[Key]
public int AssetTypeId { get; set; }
[Required]
public string Name { get; set; }
}

Answer Source

Please note that the InMemory provider is not a production ready relational database provider and was only provided to assist with integration tests.

It won't (necessarly) enforce the consistency of your data or ensure that the constrains are fulfilled like a real database provider would do.

This is clearly documented and expected behavior, please read the documentation "Test with InMemory"

InMemory is not a relational database

EF Core database providers do not have to be relational databases. InMemory is designed to be a general purpose database for testing, and is not designed to mimic a relational database.

Some examples of this include:

  • InMemory will allow you to save data that would violate referential integrity constraints in a relational database.
  • If you use DefaultValueSql(string) for a property in your model, this is a relational database API and will have no effect when running against InMemory. Tip

For many test purposes these difference will not matter. However, if you want to test against something that behaves more like a true relational database, then consider using SQLite in-memory mode.

That being said: If you really need to validate the constraints, follow the documentation's recommendation and use Sqlite in memory database instead for your integration tests.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download