After we added sessions in our asp web app, now our controller unit tests fail:
data_browser.Tests.HomeControllerTests.Index [FAIL]
System.NullReferenceException : Object reference not set to an instance of an object
Tests fail on a statement where we use sessions:
HttpContext.Session.SetString("games", JsonConvert.SerializeObject(games));
Looks like as a session service needs to be added. In our app it is done through Startup class's methods:
public void ConfigureServices(IServiceCollection services)
{
// Add MVC services to the services container.
services.AddMvc();
services.AddSession();
services.AddCaching();
}
and
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseSession();
...
}
But when we unit test, we just instantiate our controller smth like this:
HomeController homeCtrler = new HomeController();
JsonResult jsonResult = (JsonResult)homeCtrler.Smth();
Assert.Eq(bla, bla);
So is there a way to inject sessions for controller in asp.net 5 unit tests?
An example (from https://github.com/aspnet/MusicStore/blob/master/test/MusicStore.Test/ShoppingCartControllerTest.cs):
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Session = new TestSession();
var controller = new ShoppingCartController()
controller.ActionContext.HttpContext = httpContext;
// Act
var result = await controller.Index();
//--------------
class TestSession : ISession
{
private Dictionary<string, byte[]> _store
= new Dictionary<string, byte[]>(StringComparer.OrdinalIgnoreCase);
public IEnumerable<string> Keys { get { return _store.Keys; } }
public void Clear()
{
_store.Clear();
}
public Task CommitAsync()
{
return Task.FromResult(0);
}
public Task LoadAsync()
{
return Task.FromResult(0);
}
public void Remove(string key)
{
_store.Remove(key);
}
public void Set(string key, byte[] value)
{
_store[key] = value;
}
public bool TryGetValue(string key, out byte[] value)
{
return _store.TryGetValue(key, out value);
}
}
Related
I am new to ReactiveUI and trying to test a view model that looks like this:
public interface IService
{
Task<SessionModel> GetData(string id);
}
/// Provides a group of schedulers available to be used
public interface ISchedulers
{
IScheduler Default { get; }
IScheduler Dispatcher { get; }
}
public class MyVm : ReactiveObject
{
IService service;
public MyVm(ISchedulers schedulers, IService service)
{
this.service = service;
this.session = this.WhenAnyValue(x => x.SessionId)
.SelectMany(SearchSession)
.ObserveOn(schedulers.Default)
.ToProperty(this, x => x.Session);
}
private async Task<SessionModel> SearchSession(string id)
{
return await this.service.GetData(id);
}
private string sessionId;
public string SessionId
{
get => sessionId;
set => this.RaiseAndSetIfChanged(ref sessionId, value);
}
readonly ObservableAsPropertyHelper<SessionModel> session;
public SessionModel Session
{
get { return session.Value; }
}
}
public class SessionModel { }
I'm mocking the service call to return dummy data, but not sure what I need to do with a TestScheduler in order to get the SelectMany to work.
Here's a test class that shows how i would create a test for the view model. The goal is to eventually be able to check that the model got set:
[TestClass]
public class MyVmTests
{
[TestMethod]
public void CreateClass
{
var subject = new MyVm(/*pass in mocks*/);
subject.SessionId="test";
Assert.IsNotNull(subject.Session);
}
}
I don't think using TestScheduler is necessary. The following passes for me (using Moq):
var mockSchedulers = new Mock<ISchedulers>();
mockSchedulers.Setup(s => s.Default).Returns(Scheduler.Immediate);
var id = "123";
var mockService = new Mock<IService>();
var returnSession = new SessionModel();
mockService.Setup(s => s.GetData(It.Is<string>(i => i == id)))
.ReturnsAsync(returnSession);
var target = new MyVm(mockSchedulers.Object, mockService.Object);
target.SessionId = id;
Assert.IsNotNull(target.Session);
Assert.AreEqual(returnSession, target.Session);
TestScheduler is best when you're trying to test something with time (like a Delay, proving that the Delay actually happened). You're not really doing that here.
I have build a WebAPI and apart from my tests running on Postman I would like to implement some Integration/Unit tests.
Now my business logic is very thin, most of the time its more of CRUD actions, therefore I wanted to start with testing my Controllers.
I have a basic setup. Repository pattern (interfaces), Services (business logic) and Controllers.
The flow goes Controller (DI Service) -> Service (DI Repo) -> Repo Action!
So what I did was override my Startup file to change into a in memory database and the rest should be fine (I would assume) Services are added, repos are added and now I am pointing into a in memory DB which is fine for my basic testing.
namespace API.UnitTests
{
public class TestStartup : Startup
{
public TestStartup(IHostingEnvironment env)
: base(env)
{
}
public void ConfigureTestServices(IServiceCollection services)
{
base.ConfigureServices(services);
//services.Replace<IService, IMockedService>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
base.Configure(app, env, loggerFactory);
}
public override void SetUpDataBase(IServiceCollection services)
{
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = ":memory:" };
var connectionString = connectionStringBuilder.ToString();
var connection = new SqliteConnection(connectionString);
services
.AddEntityFrameworkSqlite()
.AddDbContext<ApplicationDbContext>(
options => options.UseSqlite(connection)
);
}
}
}
I wrote my first test, but the DatasourceService is not there:
The following constructor parameters did not have matching fixture data: DatasourceService datasourceService
namespace API.UnitTests
{
public class DatasourceControllerTest
{
private readonly DatasourceService _datasourceService;
public DatasourceControllerTest(DatasourceService datasourceService)
{
_datasourceService = datasourceService;
}
[Xunit.Theory,
InlineData(1)]
public void GetAll(int companyFk) {
Assert.NotEmpty(_datasourceService.GetAll(companyFk));
}
}
}
What am I missing?
You can't use dependency injection on test classes. You can only let xunit inject special fixtures via constructor (see docs).
For Integration Testing you want to use the TestServer class from Microsoft.AspNetCore.TestHost package and a separate Startup.cs class (easier to setup configuration than inheritance imho).
public class TestStartup : Startup
{
public TestStartup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureTestServices(IServiceCollection services)
{
services.Replace(ServiceDescriptor.Scoped<IService, MockedService>());
services.AddEntityFrameworkSqlite()
.AddDbContext<ApplicationDbContext>(
options => options.UseSqlite(connection)
);
}
public void Configure(IApplicationBuilder app)
{
// your usual registrations there
}
}
In your unit test project, you need to create an instance of the TestServer and perform the test.
public class DatasourceControllerTest
{
private readonly TestServer _server;
private readonly HttpClient _client;
public DatasourceControllerTest()
{
// Arrange
_server = new TestServer(new WebHostBuilder()
.UseStartup<TestStartup>());
_client = _server.CreateClient();
}
[Xunit.Theory,
InlineData(1)]
public async Task GetAll(int companyFk) {
// Act
var response = await _client.GetAsync($"/api/datasource/{companyFk}");
// expected result from rest service
var expected = #"[{""data"":""value1"", ""data2"":""value2""}]";
// Assert
// This makes sure, you return a success http code back in case of 4xx status codes
// or exceptions (5xx codes) it throws an exception
response.EnsureSuccessStatusCode();
var resultString = await response.Content.ReadAsStringAsync();
Assert.Equals(resultString, expectedString);
}
}
Now, when you call operations which write to the database, you can also check if the data is really written to the database:
[Xunit.Theory,
InlineData(1)]
public async Task GetAll(int companyFk) {
// Act
var response = await _client.DeleteAsync($"/api/datasource/{companyFk}");
// expected result from rest service
// Assert
response.EnsureSuccessStatusCode();
// now check if its really gone in the database. For this you need an instance
// of the in memory Sqlite DB. TestServer has a property Host, which is an IWebHost
// and it has a property Services which is the IoC container
var provider = _server.Host.Services;
var dbContext = provider.GetRequiredService<ApplicationDbContext>();
var result = await dbContext.YourTable.Where(entity => entity.Id == companyFk).Any();
// if it was deleted, the query should result in false
Assert.False(result);
}
Now you can use Xunit.DependencyInjection in your tests.
namespace Your.Test.Project
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IDependency, DependencyClass>();
}
}
}
your DI-classes:
public interface IDependency
{
int Value { get; }
}
internal class DependencyClass : IDependency
{
public int Value => 1;
}
and XUnit-test:
public class MyAwesomeTests
{
private readonly IDependency _d;
public MyAwesomeTests(IDependency d) => _d = d;
[Fact]
public void AssertThatWeDoStuff()
{
Assert.Equal(1, _d.Value);
}
}
I have a Unit of Work pattern along with a Repo pattern to interact with the db layer (Entity Framework in this case) and then I have dependency injection going on in the controller's constructor. My question is, so I've mocked a IUnitOfWork which is what the controller interacts with, however, the Unit Of Work class actually accesses the repository so do I also have to mock a repository and if so, how would I implement this? I'm trying to complete a unit test of a basic Get controller method. I've read and watched several hours of video and articles and this is what I have so far:
class UrlControllerTests
{
[TestMethod]
public void ShouldReturnUrlList()
{
Mock<IUnitOfWork> fakeUnitOfWork = new Mock<IUnitOfWork>();
var urlController = new UrlController(fakeUnitOfWork.Object);
urlController.Get(5); //All this is just to see if we can get thru a test.
Assert.IsTrue(true);
}
IRepo
public interface IRepo<TEntity> where TEntity: class
{
TEntity Get(int id);
IEnumerable<TEntity> GetAll();
//Allows the running of lamba-style LINQ queries like the typical Entity Framework does:
IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);
void Add(TEntity entity);
void AddRange(IEnumerable<TEntity> entities);
void Remove(TEntity entity);
void RemoveRange(IEnumerable<TEntity> entities);
}
IUnitOfWork:
public interface IUnitOfWork :IDisposable {
IRepo<Url> Urls { get; }
int Complete();
}
UnitOfWork:
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationDbContext _context;
public IRepo<Url> Urls { get; set; }
public UnitOfWork(ApplicationDbContext context)
{
_context = context;
Urls = new Repo<Url>(_context);
}
public int Complete()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
Repo:
public class Repo<TEntity> : IRepo<TEntity> where TEntity : class
{
protected readonly ApplicationDbContext _context;
public Repo(ApplicationDbContext context)
{
_context = context;
}
public TEntity Get(int id)
{
return _context.Set<TEntity>().Find(id);
}
//Repositories SHOULD NOT return IQueryable because otherwise other resources may
//Try to build queries
public IEnumerable<TEntity> GetAll()
{
return _context.Set<TEntity>().ToList();
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return _context.Set<TEntity>().Where(predicate);
}
public void Add(TEntity entity)
{
_context.Set<TEntity>().Add(entity);
}
public void AddRange(IEnumerable<TEntity> entities)
{
_context.Set<TEntity>().AddRange(entities);
}
public void Remove(TEntity entity)
{
_context.Set<TEntity>().Remove(entity);
}
public void RemoveRange(IEnumerable<TEntity> entities)
{
_context.Set<TEntity>().RemoveRange(entities);
}
}
}
I am using moq.
EDIT: What I'm trying to test:
public IHttpActionResult Get(int id)
{
var url = _unitOfWork.Urls.Get(id);
if (url == null)
{
NotFound();
}
return Ok(url);
}
The Target Method under test is dependent on the IUnitOfWork and IRepo<Url>. When creating unit tests you would normally mock the dependencies of the system under test so that it can be tested in isolation without having dependencies calling actual implementations (Integration Tests).
[TestClass]
public class UrlControllerTests {
[TestMethod]
public void Get_With_Valid_Id_Should_Return_Url() {
//Arrange
var testId = 5;
var expected = new Url { Id = testId };
var mockRepo = new Mock<IRepo<Url>>();
mockRepo.Setup(m => m.Get(testId)).Returns(expected);
var mockUnitOfWork = new Mock<IUnitOfWork>();
mockUnitOfWork.Setup(m => m.Urls).Returns(mockRepo.Object);
var sut = new UrlController(mockUnitOfWork.Object);
//Act
var actionResult = sut.Get(testId) as OkNegotiatedContentResult<Url>;
//Assert
Assert.IsNotNull(actionResult);
Assert.AreEqual(expected, actionResult.Content);
}
}
Moq is flexible enough that you could also mock the entire dependency call so that you don't necessarily have to mock the repository if you have no need for more complicated setups.
The test would then be rewritten to
[TestClass]
public class UrlControllerTests {
[TestMethod]
public void Get_With_Valid_Id_Should_Return_Url() {
//Arrange
var testId = 5;
var expected = new Url { Id = testId };
var mockUnitOfWork = new Mock<IUnitOfWork>();
mockUnitOfWork.Setup(m => m.Urls.Get(testId)).Returns(expected);
var sut = new UrlController(mockUnitOfWork.Object);
//Act
var actionResult = sut.Get(testId) as OkNegotiatedContentResult<Url>;
//Assert
Assert.IsNotNull(actionResult);
Assert.AreEqual(expected, actionResult.Content);
}
}
the repository is a prop of an Mvc controller, i'm trying to write a test method to check this controller,
but i get an error in the container call...
i'm new in mvc and testing.. so i dont know where to start
how can i do this?
this is how the test looks like:
public void SomeTest()
{
var controller= new SomeController();
var result = SomeController.Index();
Assert.IsNotNull(result);
}
The error i recive when i run the test
an exception of type System.NullReferenceException occurred in SomeContext.dll but was not handled in user code
Has your repository been initialized?
In your controller:
private Repository Repository {get;set;}
public ActionResult Index()
{
Repository = new Repository();
var something = Repository.DoSomeWork();
return View(something);
}
In your test class:
public void SomeTest()
{
var controller = new SomeController();
var result = controller.Index();
Assert.IsNotNull(result);
}
or if you are using dependency injection, with Ninject property injection you can try using Moq to inject the class:
public class SomeController : Controller
{
private IRepository repository;
[Inject]
public IRepository Repository
{
get { return repository; }
set { repository = value; }
}
// GET: /Some/
public ActionResult Index()
{
var someCollection = Repository.SomeMethod("some parameter");
foreach (var value in someCollection)
{
ViewData["message"] += value;
}
return View(someCollection);
}
}
and the test class with moq:
public class SomeTestClass
{
private Mock<IRepository> mockRepository;
[Test]
public void GivenSometestThenExpectSomeResult()
{
// Arrange
var controller = new SomeController();
mockRepository = new Mock<IRepository>();
mockRepository.Setup(x => x.SomeMethod(It.IsAny<string>())).Returns(new List<string>());
controller.Repository = mockRepository.Object;
// Act
ActionResult result = controller.Index();
// Assert
Assert.AreEqual("Index", result.ViewName);
}
}
Am I correct to think that I have to create my controller by passing it an instance of my context AND my Service to make it testable?
For example: new Controller(mycontext,myservice)
I'm thinking this is the way I need to change my code, but I don't want to if I don't have to. Since for MVC3 to work out of the box it requires controller constructors to be parameterless, I think this means I'm going to have to go down the path of IoC. Otherwise the code in my Wizard action saves to a real DBContext even during testing.
namespace mvc3test.Controllers
{
public class WizardController : Controller
{
private DR405DBContext db;
public WizardController(DR405DBContext dbContext)
{
db = dbContext;
}
public WizardController()
{
db = new DR405DBContext();
}
public ActionResult Index()
{
var model = new WizardViewModel();
model.Initialize();
return View(model);
}
[HttpPost]
public ActionResult Index([Deserialize] WizardViewModel wizard)
{
//wizard.Steps[wizard.CurrentStepIndex] = step;
if (ModelState.IsValid)
{
//Always save.
var obj = new dr405();
//wire up to domain model;
foreach (var s in wizard.Steps)
{
Mapper.Map(s,obj,s.GetType(), typeof(dr405));
}
using (var service = new DR405Service())
{
//Do something with a service here.
service.Save(db, obj);
}
if (!string.IsNullOrEmpty(Request.QueryString["next"]))
{
wizard.CurrentStepIndex++;
}
else if (!string.IsNullOrEmpty(Request.QueryString["prev"]))
{
wizard.CurrentStepIndex--;
}
else
{
return View("Review", wizard);
}
}
else if (!string.IsNullOrEmpty(Request.QueryString["prev"]))
{
wizard.CurrentStepIndex--;
}
return View(wizard);
}
public ActionResult Review(int id)
{
var service = new DR405Service();
var dr405 = service.GetDR405ById(db, id);
var wizard = new WizardViewModel();
if (dr405 != null)
{
wizard.Initialize();
foreach (var s in wizard.Steps)
{
Mapper.Map(dr405, s, typeof(dr405), s.GetType());
}
}
return View(wizard);
}
public ActionResult Transmit()
{
return View();
}
[HttpPost]
public String Upload(HttpPostedFileBase FileData)
{
var saveLocation = Path.Combine(Server.MapPath("\\"), "returns\\" + DR405Profile.CurrentUser.TaxPayerID);
System.IO.Directory.CreateDirectory(saveLocation);
FileData.SaveAs(Path.Combine(saveLocation, FileData.FileName));
ViewBag.Message = String.Format("File name: {0}, {1}Kb Uploaded Successfully.", FileData.FileName, (int)FileData.ContentLength / 1024);
return ViewBag.Message;
}
}
}
Am I correct to think that I have to create my controller by passing it an instance of my context AND my Service to make it testable?
Kind of. That's only half of the work you need to do. The second half is to weaken the coupling between your layers by using abstractions. Your service layer needs to implement an interface which you would inject into the constructor of your controller enforcing the contract between those two and explicitly stating that the controller needs a service layer obeying this contract:
public WizardController(IMyService service)
{
this._service = service;
}
Now in your unit test go ahead and mock it using one of the multiple mocking frameworks out there (Rhino Mocks, NSubstitute, Moq, NMock, ...).
You can use setter injection instead of constructor injection on the Controller.
public class WizardController : Controller
{
public void setDBContext( DR405DBContext db) {
this.db = db;
}
}
or
You can get the database using a Service Locator and add a setter to that.
public class DBServiceLocator
{
private static DR405DBContext db = new DR405DBContext();
public static DR405DBContext locate() {
return db;
}
public static setContext(DR405DBContext db) {
DBServiceLocator.db = db;
}
}
In the setup() part of your unit test, use setters to 'inject' your mock/stub database.
Also, using an interface rather than DR405DBContext will make mocking easier.