ASP.NET MVC 2 Unit Testing Stange Errors - c#

I am working through the book Professional ASP.NET MVC 2 and I am trying to get the unit testing in chapter 1 to work correctly; howver, I am getting some very strange errors.
There are two projects in the solution: NerdDinner, and NerdDinner.Tests.
In the NerdDinner Project I have the following interface:
IDinnerRepository.cs
//...
namespace NerdDinner.Models
{
interface IDinnerRepository
{
//...
}
}
Also in the NerdDinner project, I have the following class:
//...
using NerdDinner.Models;
//...
namespace NerdDinner.Controllers
{
public class DinnersController : Controller
{
IDinnerRepository dinnerRepository;
// Default constructor
public DinnersController() : this(new DinnerRepository()){} // DinnerRepository is another concrete implementation of IDinnerRepository
//Test constructor
public DinnersController(IDinnerRepository repository) {
dinnerRepository = repository;
}
}
}
In the NerdDinner.Tests project, I have the following concrete implementation of IDinnerRepository:
//...
using NerdDinner.Models;
//...
namespace NerdDinner.Tests.Fakes
{
class FakeDinnerRepository : IDinnerRepository
{
//...
public FakeDinnerRepository(List<Dinner> dinners)
{
//...
}
//...
}
}
Now for the actual unit test (in NerdDinner.Tests)
using NerdDinner.Controllers;
//...
using NerdDinner.Models;
using NerdDinner.Tests.Fakes;
namespace NerdDinner.Tests
{
[TestClass]
public class DinnersControllerTest
{
List<Dinner> CreateTestDinners()
{
//...
}
DinnersController CreateDinnersController()
{
return new DinnersController(new FakeDinnerRepository(CreateTestDinners()));
}
}
}
And now for the actual problem:
In the method CreateDinnersController in the class DinnerControllerClass, I am getting the following error:
DinnersController.DinnersController(NerdDinner.Models.IDinnerRepository repository) (+ 1 overload(s))
Error:
The best overloaded method match for 'NerdDinner.Controllers.DinnersController.DinnersController(NerdDinner.Models.IDinnerRepository)' has some invalid arguments.
It gives me the option to create a constructor stub in DinnersController. It generates the following code:
private global::NerdDinner.Tests.Fakes.FakeDinnerRepository repository;
//...
public DinnersController(global::NerdDinner.Tests.Fakes.FakeDinnerRepository repository)
{
// TODO: Complete member initialization
this.repository = repository;
}
Even after generating that code, I still get the same error. But why should I even need that code anyway? As far as I can tell, I am doing everything correctly.
Can anybody help me figure out what is going on here?
Edit
The generated code is giving the following error:
The type or namespace 'Tests' does not exist in the namespace 'NerdDinner' (are you missing any assembly reference?)

From what you've shown the IDinnerRepository interface is not public meaning that it is not visible from your unit test. I would recommend you making it public as I suspect you have two different interfaces : one defined in the unit test and one in your project which conflict. Also I would recommend you to avoid relying on Visual Studio generate all the crap reflection code in order to test private and internal members.

The last error you're getting is due to there being no reference from the production code to the test code - but that's appropriate. You don't want that extra constructor.
Instead, you need to find out why the existing constructor taking an IDinnerRepository isn't being used. Are you sure you only have one interface called IDinnerRepository? If you go to the FakeDinnerRepository source, go to the declaration, put the cursor in IDinnerRepository and hit F12 (go to definition) does it go to the right place?
If you add a new member to IDinnerRepository (just for the sake of testing: void Foo(); would be fine) does it cause both the production and fake implementation to fail to compile?

Related

Referencing of classes and namespaces Visual Studio 2017

I noticed for my little project that when importing classes some use full folder reference while otheres don't.
Here is code from project Mini that i am working on.
Models folder
Contains two entities, Auto and Airplane
namespace Mini.Models {
public class Auto {
// code and stuff
}
}
namespace Mini.Models {
public class Airplane {
// code and stuff
}
}
Services folder Contains single service class
namespace Mini.Services
{
public class AutoService : IAutoService {
public bool Get() {
var autoObject = new Models.Auto(); // notice how it references Models folder
var planeObject = new Airplane(); // Same folder but not referencing Models in front of it
// other code
}
}
public interface IAutoService {
bool Get();
// others
}
}
While not a major bugbear, it is still annoying that two classes in same folder get referenced differently, and i cannot figure out why.
Any advice would be appreciated.
Error Message when removing Models folder
Error CS0118: 'Auto' is a namespace but is used like a type (34, 27)
Based on the error message you have provided:
Error CS0118: 'Auto' is a namespace but is used like a type (34, 27)
It would appear that you have a namespace called Auto. Imagine the following example:
namespace MyApp.Auto
{
class Test
{
}
}
namespace MyApp
{
class Auto
{
}
class MyTest
{
private Auto test;
}
}
Because you can see, from the MyApp namespace, both a class called Auto and a namespace called Auto (either namespace MyApp.Auto or simply namespace Auto), C# isn't sure which one you want. As such, it's forcing you to be specific in choosing one or the other.
The easiest solution is to change the MyApp.Auto namespace to something else.
This is not fix but explaining with proper code sample (and why ).
namespace Mini.Models
{
public class Auto
{
// code and stuff
}
}
namespace Mini.Models
{
public class Airplane
{
// code and stuff
}
}
namespace Mini.Auto
{
public class OtherAirplane
{
// code and stuff
}
}
namespace Mini
{
using Mini.Models;
using namespaceAuto = Auto ; /// this also not fix the issue.
class NamespaceIssue
{
void execute()
{
var autoObject = new Auto(); // Error
var planeObject = new Airplane(); // Same folder but not referencing Models in front of it
// other code
}
}
}
now you can see some were in code you have "Mini.Auto" namespace , and it is couching issue.
i tested for VS 2015 have same issue. maybe we have to report to VS team or it is by design .
The issue seemed to be with VS2017 or the way it created the project first time around.
Upon starting brand new project (ASP Core 2.2, Web API, with https enabled and docker disabled), and using same classes the issue was non-existant.

Accessibility: Parameter type is less accessible than method

this question has been answered a few times but I still can not get my code to work. I have a very simple class looking like this:
namespace SportsStore.Controllers {
public class ProductController : Controller {
private IProductRepository repository;
public ProductController(IProductRepository repo) {
repository = repo;
}
public ViewResult List() => View(repository.Products);
}
}
With IproductRepository.cs
namespace SportsStore.Models
{
interface IProductRepository {
IQueryable<Product> Products { get; }
}
}
The error I get is of course is this one:
Severity Code Description Project File Line Suppression State
Error CS0051 Inconsistent accessibility: parameter type
'IProductRepository' is less accessible than method
'ProductController.ProductController(IProductRepository)' SportsStore
I can see that the problem is that I shouldn't set the IproductRepository variable to private and then have the ProductController public.
I read this answer:
Inconsistent Accessibility: Parameter type is less accessible than method
and it basically tells me that the class needs to be public (it already is) and the parameter needs to be 'as accessible as'. Meaning public, right?
But if I set the parameter to public aswell, like this:
public IProductRepository repository;
Then I still get this error:
Severity Code Description Project File Line Suppression State
Error CS0052 Inconsistent accessibility: field type 'IProductRepository'
is less accessible than field 'ProductController.repository' SportsStore
How do I fix this?
Note: This is an example from the book Asp Net Core by Adam Freeman and the github for the project is available at this link, in which the code is witten as I have above, so they have made no corrections to it. It seems like it should be working. Am running Visual Studio 2017 if that makes any difference.
https://github.com/Apress/pro-asp.net-core-mvc-2/blob/master/08%20-%20SportsStore/SportsStore/SportsStore/Controllers/ProductController.cs
Looks like the interface should be public
namespace SportsStore.Models
{
public interface IProductRepository
{
IQueryable<Product> Products { get; }
}
}
Same applies for classes that you pass to the constructor.

No type was found that matches the controller named ***

I am working on an ASP.NET MVC app using oData 4. Up until yesterday they project was in VB.NET. We are now finally attempting to move from VB.NET to C#. I've tried to introduce 2 controllers written in C# into the project and in both instances, receive the dreaded "No type was found that matches the controller named ***" error when attempting to access either controller.
The first one was a new controller. It looks like this:
using System.Data.Entity.Infrastructure;
using System.Net;
using System.Web.Http;
using System.Web.OData;
namespace Controllers
{
public class ORDER_LINE_SUMMARYController : ODataController
{
private Entities db = new Entities();
[EnableQuery()]
public IQueryable<ORDER_LINE_SUMMARY> GetORDER_LINE_SUMMARY()
{
return db.ORDER_LINE_SUMMARY;
}
[EnableQuery()]
public SingleResult<ORDER_LINE_SUMMARY> GetORDER_LINE_SUMMARY([FromODataUri()] decimal key)
{
return SingleResult.Create(db.ORDER_LINE_SUMMARY.Where(ORDER_LINE_SUMMARY => ORDER_LINE_SUMMARY.ID == key));
}
protected override void Dispose(bool disposing)
{
if ((disposing))
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool ORDER_LINE_SUMMARYExists(decimal key)
{
return db.ORDER_LINE_SUMMARY.Count(e => e.ID == key) > 0;
}
}
}
In order to make certain that I didn't miss a step in incorporating this new controller, I also tried replacing an existing VB.NET controller with a C# controller and receive the same error. Except for the entity name, the code is essentially identical to the one above, so I'm not positing it here for brevity's sake.
One interesting note, and I think this is germane to the underlying problem. When I added the new C# controller to replace the existing VB.NET one I hadn't yet removed the VB.NET controller from the project. Obviously the class names for both are the same, however; the .NET compiler didn't error or otherwise complain about this. I suspect this issue may be related to attempting to mix C# and VB.NET within the same assembly due to this.
Any suggestions or ideas would be greatly appreciated. TIA.

Test Service using Entity Framework without dependency injection

I'm trying to test business logic in queries in services. So I don't want my tests to have real access to the database, because they are unit tests, not integration tests.
So I've made a simple example of my context and how I'm trying to shim it.
I have an entity
public class SomeEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
and a service
public class Service
{
public int CountSomeEntites()
{
using (var ctx = new Realcontext())
{
int result = ctx.SomeEntities.Count();
return result;
}
}
}
And this is the real context
public partial class Realcontext : DbContext
{
public virtual DbSet<SomeEntity> SomeEntities { get; set; }
public Realcontext() : base("name=Realcontext")
{
InitializeContext();
}
partial void InitializeContext();
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
}
So I've tried to create a fake context and I detourned the constructor of the real context in my test method
This is the fake context
public class FakeContext : DbContext
{
public DbSet<SomeEntity> SomeEntities { get; set; }
public FakeContext()
{
}
}
And finally the test class
[TestClass]
public class ServiceTests
{
[TestMethod]
public void CountEmployee_ShoulReturnCorrectResult()
{
using (ShimsContext.Create())
{
ShimRealcontext.Constructor = context => GenerateFakeContext();
ShimDbContext.AllInstances.Dispose = () => DummyDispose();
Service service = new Service();
int result = service.CountSomeEntites();
Assert.AreEqual(result, 2);
}
}
private FakeContext GenerateFakeContext()
{
FakeContext fakeContext = new FakeContext();
fakeContext.SomeEntities.AddRange(new[]
{
new SomeEntity {Id = 1, Name = "entity1"},
new SomeEntity {Id = 2, Name = "entity2"}
});
return fakeContext;
}
}
When I run the test, the RealContext constructor is returned properly, a FakeContext is built in the GenerateFakeContext() method, it contains 2 SomeEntities and it is returned, but right after, in the service, the property SomeEntities of the variable ctx equals to null.
Is it because my variable ctx is declared as a new RealContext()? But calling the constructor of RealContext returns a FakeContext(), so isn't the variable supposed to be of type FakeContext?
Am I doing something wrong? Or is there any other way to test the service without accessing the real database?
I had the simlair situation and I solved it with build configuration and conditional compilation. It's not the best solution, but it worked for me and solved the problem. Here is the receipt:
1. Create DataContext interface
First you need to create an interface which will be implemented by both context classe you going to use. Let it be named just 'IMyDataContext'. Inside it you need to describe all DbSets you need to have access to.
public interaface IMyDataContext
{
DbSet<SomeEntity> SomeEntities { get; set; }
}
And both your context classes need to impelemt it:
public partial class RealDataContext : DataContext, IMyDataContext
{
DbSet<SomeEntity> SomeEntities { get; set; }
/* Contructor, Initialization code, etc... */
}
public class FakeDataContext : DataContext, IMyDataContext
{
DbSet<SomeEntity> SomeEntities { get; set; }
/* Mocking, test doubles, etc... */
}
By the way you can even make properies read-only at interface level.
2. Add 'Test' build configuration
Here you can find how to add new build configuration. I named my configuratin 'Test'. After new configuration is created, go to your DAL project properties, Build section on the left pane. In the 'Configuration' drop-down select the configuration you've just created and in input 'Conditional compilation symbols' type 'TEST'.
3. Incapsulate context injection
To be clear, my approach is still method/property based DI solution =)
So now we need to implement some injection code. For simplicity you can add it right into your service or extract into another class if you need more abstraction. The main idea is to use conditional compilation direcitves instead of IoC framework.
public class Service
{
// Injector method
private IMyDataContext GetContext() {
// Here is the main code
#if TEST // <-- In 'Test' configuration
// we will use fake context
return new FakeDataContext();
#else
// in any other case
// we will use real context
return new RealDataContext();
#endif
}
public int CountSomeEntites()
{
// the service works with interface and does know nothing
// about the implementation
using (IMyDataContext ctx = GetContext())
{
int result = ctx.SomeEntities.Count();
return result;
}
}
}
Limitations
The described approach solves the the problem you described, but it has a limitation: as IoC allows you switch contexts dynamically at runtime, conditional complation requires you to recompile the solution.
In my case it's not a problem - my code isn't covered by tests for 100% and I don't run them on each build. Usually I run tests only before commiting the code, so it's very easy to switch the build configuration in VS, run tests, make sure that nothing was broke and then return to debug mode. In release mode you don't need to run test either. Even if you need - you can craete "Release build test mode" configuration and continue to use the same solution.
Another problem is if you have continuos integration - you need to make additional setup to your build server. Here you have two ways:
Setup two build definitions: one for release and one for tests. If your server is setup to automatic release you need to be careful because test fails will be shown in the second one while the first is deployed.
Set complex build definition which builds your code in Test configuration for the first time, runs test and if they are OK - then recompiles the code in target configuration and prepare to deploy.
Thus, as any solution this one is yet another compromise between simplisity and flexibility.
UPDATE
After some time I understand that the way I described above is very heavy. I mean - build configurations. In case of only two IDataContext implementations: 'Core' and 'Fake' you can simply use bool paramenter and simple if/else branches instead of compilation directives #if/#else/#endif and all the head ache configuring your build server.
If you have more than two implementations - you can use enum and switch block. A probem here is to define what you will return in default case or if value is out of enum's range.
But the main benefit of such approach is that you can be no longer bound to compilation time. Injector parameter could be changed at any time, for example using web.config and ConfigurationManager. Using it you could event switch your data context at run time.

Testing MassTransit Subsciber

I need to test my subscriber which is using MassTransit.
Below is a sample code :
using System;
using MassTransit;
public class AnimalSubscriber : Consumes<Animal>.Context
{
public void Consume(IConsumeContext<Animal> message)
{
//.. my code here..
}
}
Right now I have not Idea how to test the Subscriber. If someone could let me know some details; that would be very helpful!
As of now, foolishly I thought of creating a Object of AnimalSubscriber and call the Consume method.
[TestFixture]
public class Test
{
[Test]
public void SearchAnimals()
{
AnimalSubscriber subscriber = new AnimalSubscriber();
Animal request = new Animal
{
Id : 1,
Name : "Tiger"
};
//Not sure how to mock this IReceiveContext.
IReceiveContext context = new ReceiveContext();
IConsumeContext<Animal> message =new ConsumeContext<Animal>(context, request);
subscriber.Consume(null);
}
}
But I got stuck with the below line of code :
IConsumeContext<Animal> message =new ConsumeContext<Animal>(context, request); //<- Not sure how to mock this IReceiveContext.
Error : The type 'MassTransit.Context.ReceiveContext' has no
constructors defined
Need some advice please!
If you are using MassTransit v2 (which based on the interfaces you've specified above is indeed the case), you can use the Testing namespace to build out your tests.
An example unit test is available in the v2 repository branch: https://github.com/MassTransit/MassTransit/blob/v2-master/src/MassTransit.Tests/Testing/ConsumerTest_Specs.cs#L19
As an example, a testing fixture can be setup to build your consumer and send messages to it:
_test = TestFactory.ForConsumer<AnimalSubscriber>()
.InSingleBusScenario()
.New(x =>
{
x.ConstructUsing(() => new AnimalSubscriber());
x.Send(new Animal(), (scenario, context) => context.SendResponseTo(scenario.Bus));
});
_test.Execute();
Then you can build assertions around that test, such as:
_test.Sent.Any<A>().ShouldBeTrue();
Note that this only works with v2, the Testing namespace, while present in v3, doesn't exactly work yet (it didn't survive the move to async and I haven't taken the time to get it entirely working yet).

Categories

Resources