Skip to content
This repository has been archived by the owner on Mar 11, 2021. It is now read-only.

Improve coverage of WorkItemType and WorkItem #240

Merged
merged 31 commits into from Sep 23, 2016
Merged

Conversation

kwk
Copy link
Collaborator

@kwk kwk commented Sep 12, 2016

Click here and select the Files tab to see how much the coverage is increased per directory or file with this PR.

Fixes #216
Fixes #217

@kwk kwk added the 💣 test label Sep 12, 2016
@kwk kwk self-assigned this Sep 12, 2016
@kwk
Copy link
Collaborator Author

kwk commented Sep 13, 2016

So far, the overall (unit+integration) coverage is bumped from 23.6% to 28.3% with the current tests. It may not seem like much but it is tedious.

@kwk
Copy link
Collaborator Author

kwk commented Sep 15, 2016

So far, the overall (unit+integration) coverage is bumped from 23.6% to 33.0% with the current tests.

@codecov-io
Copy link

codecov-io commented Sep 16, 2016

Current coverage is 47.67% (diff: 100%)

Merging #240 into master will increase coverage by 10.81%

@@             master       #240   diff @@
==========================================
  Files            32         33     +1   
  Lines          1199       1204     +5   
  Methods           0          0          
  Messages          0          0          
  Branches          0          0          
==========================================
+ Hits            442        574   +132   
+ Misses          692        589   -103   
+ Partials         65         41    -24   

Powered by Codecov. Last update 84cfce3...7b84ac8

t.Parallel()
resource.Require(t, resource.UnitTest)
e := NotFoundError{entity: "foo", ID: "bar"}
assert.Equal(t, fmt.Sprintf(stNotFoundErrorMsg, e.entity, e.ID), e.Error())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite sure what you're testing here...

Copy link
Collaborator Author

@kwk kwk Sep 20, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aslakknutsen, I test the Error() method of the NotFoundError struct.

Those tests seemed like low handing fruits.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that part. But what your verifying is the formatting of the error string message. We have no strict requirement around these messages at this time and they are highly subject to rapid change. Adding test code to this part is only asking for test failure on any minuscule code change moving forward. If you wanted to 'touch' the errors to get coverage, a better strategy would be to test the scenarios/paths where they are used. That probably has more meaning.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I test the format, yes. But I'm also testing the errors as a unit. And even if we don't have a strict format right now. We might have it in the future and then we have a test for this. I strongly believe in what you said about scenarios and paths. But then I'm doing an integration test. So far I just wanted to test the errors unit. We can still layer tests that go through different packages as we go.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error is not a unit in my world. It has no logic.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure it does. It's a struct with functions on it. But no matter what the opinion is, the coverage output sees logic in there. Let's see if we run into troubles when updating the errors and talk about the concerns then, okay?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The concern is already present: The addition of random tests that is purely there to satisfy a coverage report with little real value to the code base and only add a maintenance overhead.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aslakknutsen Is this your concern for the rest of this PR? Then I have wasted days basically.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in general what we should test is the intent of our code. The exact text of the error messages is not something the world knows or cares about. Therefore it can change without breaking the program. A meaningful test here would be that we we get back a message of >0 length and not nil, that the code doesn't crash for expected inputs, etc.

Copy link
Contributor

@tsmaeder tsmaeder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some comments/suggestions, but I can live with what's there.

// DummyEqualer implements the Equaler interface and can be used by tests.
// Other than that it has not meaning.
type DummyEqualer struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't understand what we need this struct for.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems it's only used for testing, if so, could we move it to one of the "_test.go" files?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is used in all the tests from different packages to test the type difference of two Equaler implementations. See here for example.

@@ -2,6 +2,11 @@ package models

import "fmt"

const(
stBadParameterErrorMsg = "Bad value for parameter '%s': '%v'"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why make a constant for sth that is used exactly once?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is used in the error test to ensure the message is correct. This is what @aslakknutsen complained about because it adds maintenance overhead. My hope is that it pays off in the long run.

)

func TestListFieldDefMarshalling(t *testing.T) {
t.Parallel()
resource.Require(t, resource.UnitTest)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm really unclear on what the concept behind "require" should be. "Database" is a resource we need to execute certain tests. But "UnitTest" is not a resource that can be present or absent. It's more of a label. Is there ever a reason do skip this test? I don't think so.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the whole idea was to decide at runtime if a test shall be executed, remember? I know that is a bit strange that a Unit test is a resource. In fact if you look at the Require() implementation you can already see that UnitTest is treated in a special way. But it is not subject of this PR to discuss this implementation. Feel free to open another PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've given some thought to the resource concept. I think we would be better off if we'd call it "precondition". Then resource.Database would become precondition.HAVE_DATABASE and resource.UnitTest would become precondition.RunUnitTest. The resource.Require() function could be renamed to precondition.IsFulfilled().

In total this means:

precondition.IsFulfilled(t, precondition.RunUnitTest)

Does this sound better @tsmaeder ? Feel free to create a PR an reference this discussion (#240 (comment)).

stDuration = SimpleType{KindDuration}
stURL = SimpleType{KindURL}
stList = SimpleType{KindList}
stString = SimpleType{Kind: KindString}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this for consistency reasons or are you saying "SimpleType{KindString}" is bad?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest: I'm not sure. Most of the time to be explicit is a good thing in Go. That's probably why I've written it out.

@@ -46,12 +46,17 @@ func (fieldType ListType) ConvertFromModel(value interface{}) (interface{}, erro

type converter func(FieldType, interface{}) (interface{}, error)

const (
stErrorNotArrayOrSlice = "value %v should be array/slice, but is %s"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I don't think these should be constants if used only once.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment above.

SimpleType: SimpleType{Kind: KindList},
ComponentType: SimpleType{Kind: KindInteger},
}
assert.False(t, a.Equal(c))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a case where the two objects are equal:
assert.True(t, a.Equal(c))
assert.True(t, c.Equal(a)) // test for inverse

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See 7b84ac8

t.Parallel()
resource.Require(t, resource.UnitTest)
e := NotFoundError{entity: "foo", ID: "bar"}
assert.Equal(t, fmt.Sprintf(stNotFoundErrorMsg, e.entity, e.ID), e.Error())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in general what we should test is the intent of our code. The exact text of the error messages is not something the world knows or cares about. Therefore it can change without breaking the program. A meaningful test here would be that we we get back a message of >0 length and not nil, that the code doesn't crash for expected inputs, etc.

@kwk kwk changed the title WIP Improve coverage of WorkItemType and WorkItem Improve coverage of WorkItemType and WorkItem Sep 23, 2016
This follows steps 1 and 2 of the article: "5 simple tips and tricks for
writing unit tests in #golang" which can be found here:

https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742

Shameless quote from the article above to justify this change:

> 1. Put your tests in a different package
> Go insists that files in the same folder belong to the same package,
> that is except for `_test.go` files. Moving your test code out of the
> package allows you to write tests as though you were a real user of the
> package. You cannot fiddle around with the internals, instead you focus
> on the exposed interface and are always thinking about any noise that
> you might be adding to your API.
>
> This frees you up to change the internals however you like without
> having to adjust your test code.
>
> 2. Internal tests go in a different file
> If you do need to unit test some internals, create another file with
> `_internal_test.go` as the suffix. Internal tests will necessarily be
> more brittle than your interface tests — but they’re a great way to
> ensure internal components are behaving, and are especially useful if
> you do test-driven development.
Also:

* removed build tags
* properly renamed test files to blackbox and whitebox tests
Some tests were failing due to wrong whitebox assumptions.

I'v re-written the initialization of unexported struct members from the
package under test to use a New* ctor method.
@kwk kwk merged commit 1efe0b9 into master Sep 23, 2016
@kwk kwk deleted the kwk-improve-coverage branch September 23, 2016 15:39
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants