views:

307

answers:

7

Suppose you have a method:

public void Save(Entity data)
{
    this.repositoryIocInstance.EntitySave(data);
}

Would you write a unit test at all?

public void TestSave()
{
    // arrange
    Mock<EntityRepository> repo = new Mock<EntityRepository>();
    repo.Setup(m => m.EntitySave(It.IsAny<Entity>());

    // act
    MyClass c = new MyClass(repo.Object);
    c.Save(new Entity());

    // assert
    repo.Verify(m => EntitySave(It.IsAny<Entity>()), Times.Once());
}

Because later on if you do change method's implementation to do more "complex" stuff like:

public void Save(Entity data)
{
    if (this.repositoryIocInstance.Exists(data))
    {
     this.repositoryIocInstance.Update(data);
    }
    else
    {
     this.repositoryIocInstance.Create(data);
    }
}

...your unit test would fail but it probably wouldn't break your application...

Question

Should I even bother creating unit tests on methods that don't have any return types* or **don't change anything outside of internal mock?

+1  A: 

The general rule of thumb is, that you test all things, that could probably break. If you are sure, that the method is simple enough (and stays simple enough) to not be a problem, that let it out with testing.

The second thing is, you should test the contract of the method, not the implementation. If the test fails after a change, but not the application, then your test tests not the right thing. The test should cover cases that are important for your application. This should ensure, that every change to the method that doesn't break the application also don't fail the test.

Mnementh
In this case, this method doesn't need a unit test, since not breaking the app, test do break. I'm testing imeplementation in this case, not the contract that's not present in the scope of the method.
Robert Koritnik
+1  A: 

A method that does not return any result still changes the state of your application. Your unit test, in this case, should be testing whether the new state is as intended.

jeyoung
New state within unit test depends solely on the mock... That's why I have second thoughts. If all my method does is col into repository and that one is mocked within a test it will have predictable result based on mock not real life situation. It will ALWAYS pass.
Robert Koritnik
You should not be testing the mock, but rather the operation that interacts with the mock. Say, you have an object `X` with a method `M()` that calls the repository. You want to test that the call to the repository is actually made, so you write a mock of the repository, then call your `X.M()` method. The test should trigger a change in the mock, which would prove that your method `M()` works as required. When you have an real implementation of the repository, then you test that implementation (not the mock) -- you would then be testing the repository.
jeyoung
@jeyoung: but that's a separate unit test. It's a unit test of the repository not the class that calls the repository as per code above... But yes. the thing is I don't test DAL since it's generated code. And expected to work as it should. And I don't intend to unit test it.
Robert Koritnik
I see... The unit test is still valid though, as there could be other changes that could break your application.
jeyoung
+1  A: 

"your unit test would fail but it probably wouldn't break your application"

This is -- actually -- really important to know. It may seem annoying and trivial, but when someone else starts maintaining your code, they may have made a really bad change to Save and (improbably) broken the application.

The trick is to prioritize.

Test the important stuff first. When things are slow, add tests for trivial stuff.

S.Lott
You may be right in the last sentence (+1 for that). The main problem here is that test will become invalid, not the code, which will evolve into a better version.
Robert Koritnik
+6  A: 

Don't forget that unit tests isn't just about testing code. It's about allowing you to determine when behaviour changes.

So you may have something that's trivial. However, your implementation changes and you may have a side effect. You want your regression test suite to tell you.

e.g. Often people say you shouldn't test setters/getters since they're trivial. I disagree, not because they're complicated methods, but someone may inadvertently change them through ignorance, fat-finger scenarios etc.

Given all that I've just said, I would definitely implement tests for the above (via mocking, and/or perhaps it's worth designing your classes with testability in mind and having them report status etc.)

Brian Agnew
I agree with what you said about regression tests. And I would also say integration tests would show some non working parts as well. But since I don't create unit tests for my DAL (it's generated) I suppose I shouldn't create unit tests unless I change my method to have some return type (like bool) to report status.
Robert Koritnik
I would say 'regression' covers both unit and integration - it indicates that they run continuously (say on every build). As to testing automatically generated code, I think that's a decision that could go either way, depending on your faith in the generation process, how complex it is etc.
Brian Agnew
+2  A: 

It's true your test is depending on your implementation, which is something you should avoid (though it is not really that simple sometimes...) and is not necessarily bad. But these kind of tests are expected to break even if your change doesn't break the code.

You could have many approaches to this:

  • Create a test that really goes to the database and check if the state was changed as expected (it won't be a unit test anymore)
  • Create a test object that fakes a database and do operations in-memory (another implementation for your repositoryIocInstance), and verify the state was changed as expected. Changes to the repository interface would incurr in changes to this object as well. But your interfaces shouldn't be changing much, right?
  • See all of this as too expensive, and use your approach, which may incur on unnecessarily breaking tests later (but once the chance is low, it is ok to take the risk)
Samuel Carrijo
Your answer is so far the closest to what I wanted to know. But you second bullet would in this case test repository and not method itself, wouldn't it?
Robert Koritnik
I don't think so. This could be an object that contains a List<Entity> (or maybe a Set, depends on what you want) and the "EntitySave" would just add the entity (if exists? I don't know how it's supposed to work). No joining multiple tables, or other complicated stuff your repository might do.
Samuel Carrijo
Your object implementation would be more costly, as mentioned, but IMHO it's the best way to see if the call was made in a consistent way.
Samuel Carrijo
Well I suppose this would be covered in integration tests because there wouldn't be any mocked classes.
Robert Koritnik
That's right. Integration tests would get a bug in this. But it would be harder to detect where's the bug. The purpose of unit testing is to find a bug cause fast. Also, they usually run faster (and more frequently). A breaking change would be detected and corrected faster. You just decide if it's worth the price ;)
Samuel Carrijo
+1  A: 

When there isn't an assertion in a method, you are essentially asserting that exceptions aren't thrown.

I'm also struggling with the question of how to test public void myMethod(). I guess if you do decide to add a return value for testability, the return value should represent all salient facts necessary to see what changed about the state of the application.

public void myMethod()

becomes

 public ComplexObject myMethod() { 
 DoLotsOfSideEffects()
 return new ComplexObject { rows changed, primary key, value of each column, etc };
 }

and not

public bool myMethod()  
  DoLotsOfSideEffects()
  return true;
MatthewMartin
The changes in the state of an object are eventually reflected externally, either by the different outcome of a behaviour of the object or by the different result that it returns when its knowledge is queried. It is also at these points that the object state becomes relevant. Therefore, the way to test methods that change internal state is to test for these changes in behaviour or knowledge at the points where they become visible.
jeyoung
@jeyoung: If I understand you correctly, these kind of tests go beyond unit testing... They go deeper than the unit being tested, don't they?
Robert Koritnik
@jeyoung. You you're saying to test "public void saveRow()", you have to invoke "public DataRow loadRow()"?
MatthewMartin
+1  A: 

The short answer to your question is: Yes, you should definitely test methods like that.

I assume that it is important that the Save method actually saves the data. If you don't write a unit test for this, then how do you know?

Someone else may come along and remove that line of code that invokes the EntitySave method, and none of the unit tests will fail. Later on, you are wondering why items are never persisted...

In your method, you could say that anyone deleting that line would only be doing so if they have malign intentions, but the thing is: Simple things don't necessarily stay simple, and you better write the unit tests before things get complicated.

It is not an implementation detail that the Save method invokes EntitySave on the Repository - it is part of the expected behavior, and a pretty crucial part, if I may say so. You want to make sure that data is actually being saved.

Just because a method does not return a value doesn't mean that it isn't worth testing. In general, if you observe good Command/Query Separation (CQS), any void method should be expected to change the state of something.

Sometimes that something is the class itself, but other times, it may be the state of something else. In this case, it changes the state of the Repository, and that is what you should be testing.

This is called testing Inderect Outputs, instead of the more normal Direct Outputs (return values).

The trick is to write unit tests so that they don't break too often. When using Mocks, it is easy to accidentally write Overspecified Tests, which is why most Dynamic Mocks (like Moq) defaults to Stub mode, where it doesn't really matter how many times you invoke a given method.

All this, and much more, is explained in the excellent xUnit Test Patterns.

Mark Seemann
I don't think you get the idea of Unit tests and mocking. If a void method only changes internal state of some other object and you write a unit test for it, you will be writing a mock for that internal object. You'll have to write its behaviour. So your method will not be changing any state. It will just use your preprogrammed mock behaviour. Barely verifying calls may be problematic in a way I explained in my question. Meaning that a test is not valid, not the code that unit test tests.
Robert Koritnik
@Robert Koritnik: I think that's the first first time someone claimed that I don't understand unit tests and Test Doubles... I agree that if a method *only* changes *internal* state, there's no reason to write a unit test for it, but then again, what reason would there be for having such code at all? I may have jumped to conclusions from your question, but I assumed that the member variable in question represented a Repository, and a Repository very much represents *external* state in my book.
Mark Seemann