0

I am writing unit tests for an ASP.NET MVC application in C# using NUnit and Rhino Mocks. I am having some trouble testing this method:

public void Install()
    {
        Database.SetInitializer<DraftOrderObjectContext>(null);
        var dbScript = CreateDatabaseInstallationScript();

        Database.ExecuteSqlCommand(dbScript);
        SaveChanges();
    }

Just to clarify, Database is not referring to a local object. The first one "Database.SetInitializer..." refers to:

System.Data.Entity.Database

and the second "Database.ExecuteSqlCommand..." refers to:

System.Data.Entity.DbContext.Database

Since the method does not return anything, I figured it would be sufficient to make a mock and verify that Database.ExecuteSqlCommand(dbScript); was called at least once.

Now I've done this before, but that involved passing a database context to the method, which is easy enough to mock, however, in this case there are no parameters. I need to somehow find a way to mock 'Database'.


I have tried straight up assigning a mock like so:

System.Data.Entity.DbContext.Database = MockRepository.GenerateMock<System.Data.Entity.DbContext.Database>();

but that breaks syntax because the property is read only.


I have also tried mocking DbContext like so:

System.Data.Entity.DbContext instance = MockRepository.GenerateMock<System.Data.Entity.DbContext>();

        instance.Expect(someCaller => someCaller.Database.ExecuteSqlCommand("sql"))
            .IgnoreArguments()
            .Return(1)
            .Repeat.Times(1);

but I get a runtime error saying DbContext.getHashCode() must return a value. I then tried stubbing the getHashCode method to make it return something but that had no effect.


I am still fairly new to mocking so I may be missing some fundamental concept here. My apologies if that is the case. Any help is much appreciated!

Reddy
  • 481
  • 1
  • 7
  • 20
  • 1
    Look into this [answer](http://stackoverflow.com/a/26017436/1346943) to a related question – Sven Grosen Oct 05 '16 at 20:17
  • hmm, I'm not sure I can use that solution as I am not able to modify the class that I need to test. – Reddy Oct 05 '16 at 20:51
  • 1
    Not sure there is a way to do this as a pure unit test - DbContext.Database is not virtual so a classic Mocking scenario won't work. Perhaps you need to draw the line there for unit tests and make this one an integration test? – Stephen Byrne Oct 05 '16 at 21:28

1 Answers1

1

I'm afraid the only answer worth giving here is that the code must be modified to be more test-friendly. Trying to write unit tests for methods that make calls to static classes, properties or methods is not a rewarding or worthwhile task. You suggest that you may be missing a fundamental concept here and this may be it: static is unit testing's worst enemy and the collective wisdom is that there isn't much point putting a lot of effort into testing things that use static resources. Just refactor the code.

If refactoring the code is truly impossible then why would you need to unit test it (this is not a rhetorical question, please comment)? If the concern is that you need to mock these objects as part of other tests, then you should wrap the evil, unmodifiable code with a test friendly interface and mock that instead.

tonicsoft
  • 1,736
  • 9
  • 22
  • To be honest with you, I have just started my first professional role as a Software Developer and have been given the task of writing unit tests for the company's custom nopCommerce plugins so I am not sure if they would feel comfortable with me actually going in and modifying core functionality that could break the entire system. But the whole wrapper thing does sound simple enough to implement, I will bring this concern up in our next meeting and just get their opinion on if they would be okay with that, or just prefer to make an integration test. – Reddy Oct 06 '16 at 12:39
  • 1
    Part of the reason why tests are a good idea is that they tell you when parts of your code may be badly designed. To be honest, if you turned up to my team meeting saying "I would like to improve this class to make it more testable" then I would be impressed and grateful. You are probably wise to discuss it first though! Good luck. p.s. in my opinion making a new developer write unit tests for code that has already been written is a bit cruel and the business value is limited. Don't let it give you a bad first impression of TDD which works best if the tests are written FIRST! – tonicsoft Oct 06 '16 at 13:04
  • Why is it that answers like this completely fail to mention that mocking things like `static` (or non-`virtual` methods, etc.) is a problem that *has been solved* in several mocking libraries already (specifically, TypeMock Isolator, JustMock, MS Fakes)? Is that fact not known, or is it just "forgotten"? Also, should we really prefer to compromise our code in order to work around limitations in some particular testing tool (eg, moq) when we need to mock something? – Rogério Oct 06 '16 at 16:28
  • Removing calls to static methods is not compromising your code, it's improving it. Also, the tools you mention should be reserved for very specific cases only. One such tool, PowerMock, states on their own github docs: "Please note that PowerMock is mainly intended for people with expert knowledge in unit testing. Putting it in the hands of junior developers may cause more harm than good." So I hardly find it appropriate to advise new developers like Reddy to use these tools as a first port of call, which is why I made no mention of them in my answer. – tonicsoft Oct 07 '16 at 08:40
  • Furthermore, these tools often work by modifying compiled code which raises the issue that you are not even testing the version of the class that is being used in production. While I don't deny that this could be useful from time to time (although personally I have never actually had the need), it just *feels* like the kind of thing that should be avoided if possible, which in this case, it is. – tonicsoft Oct 07 '16 at 08:43