go-errorcollector
collect multiple errors in golang keeping the standard error interface
Installation using go get
$ go get github.com/m90/go-errorcollector
Usage
Instantiate a new collector using New()
, collect errors using Collect(error)
and compare against nil
as usual:
errs := errorcollector.New()
for _, e := range elements {
err := mutate(e)
errs.Collect(err) // nil will be skipped
}
if errs != nil {
// handle the error
}
Error messages will be concatenated if there are multiple errors:
err := errorcollector.New()
err.Collect(errors.New("beep"))
msg := err.Error() // => "beep"
err.Collect(errors.New("boop"))
msg = err.Error() // => "collected errors: beep, boop"
You can also collect another collector:
err := errorcollector.New()
err.Collect(errors.New("rock"))
childErr := errorcollector.New()
childErr.Collect("n")
childErr.Collect("roll")
err.Collect(childErr)
msg := err.Error() // => "collected errors: rock, n, roll"
The collector satisfies the standard error
interface if you want to:
func checkForTypos(list []string) error {
err := errorcollector.New()
for _, string = range list {
err.Collect(findMistakes(string))
}
// see: https://golang.org/doc/faq#nil_error
if err != nil {
return err
}
return nil
}
Alternatively return an ErrorCollector
type than can be used just like an error:
func checkForTypos(list []string) errorcollector.ErrorCollector {
err := errorcollector.New()
for _, string = range list {
err.Collect(findMistakes(string))
}
return err
}
if err := checkForTypos("speling", "bee"); err != nil {
// do things
}
License
MIT © Frederik Ring
go-errorcollector
collect multiple errors in golang keeping the standard error interface
Installation using go get
$ go get github.com/m90/go-errorcollector
Usage
Instantiate a new collector using New()
, collect errors using Collect(error)
and compare against nil
as usual:
errs := errorcollector.New() for _, e := range elements { err := mutate(e) errs.Collect(err) // nil will be skipped } if errs != nil { // handle the error }
Error messages will be concatenated if there are multiple errors:
err := errorcollector.New() err.Collect(errors.New("beep")) msg := err.Error() // => "beep" err.Collect(errors.New("boop")) msg = err.Error() // => "collected errors: beep, boop"
You can also collect another collector:
err := errorcollector.New() err.Collect(errors.New("rock")) childErr := errorcollector.New() childErr.Collect("n") childErr.Collect("roll") err.Collect(childErr) msg := err.Error() // => "collected errors: rock, n, roll"
The collector satisfies the standard error
interface if you want to:
func checkForTypos(list []string) error { err := errorcollector.New() for _, string = range list { err.Collect(findMistakes(string)) } // see: https://golang.org/doc/faq#nil_error if err != nil { return err } return nil }
Alternatively return an ErrorCollector
type than can be used just like an error:
func checkForTypos(list []string) errorcollector.ErrorCollector { err := errorcollector.New() for _, string = range list { err.Collect(findMistakes(string)) } return err } if err := checkForTypos("speling", "bee"); err != nil { // do things }
License
MIT © Frederik Ring
In a normal scenario, whenever you identify any error during test execution, you would stop the test, fix the error and re-run the test.
But JUnit has a slightly different approach. With JUnit error collector, you can still continue with the test execution even after an issue is found or test fails. Error collector collects all error objects and reports it only once after the test execution is over.
In this tutorial, you will learn-
- What is error collector in JUnit?
- What is @Rule in jUnit?
- Example using ErrorCollector
- Benefits of JUnit ErrorCollector
Why use Error Collector?
While writing a test script, you want to execute all the tests even if any line of code fails due to network failure, assertion failure, or any other reason. In that situation, you can still continue executing test script using a special feature provided by JUnit known as “error collector.”
For this, JUnit uses @Rule annotation which is used to create an object of error collector. Once the object for error collector is created, you can easily add all the errors into the object using method addError (Throwable error). As you know, that Throwable is the super class of Exception and Error class in Java. When you add errors in this way, these errors will be logged in JUnit test result .
The benefit of adding all errors in an Error Collector is that you can verify all the errors at once. Also, if the script fails in the middle, it can still continue executing it
Note: In the case of using simple assert or try/catch block , using error collector method won’t be possible.
Sample code
To understand more on Error Collector, see below code example which demonstrates how to create an Error Collector object and add all the errors in that object to track the issue :
package guru99.junit; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ErrorCollector; public class ErrorCollectorExample { @Rule public ErrorCollector collector = new ErrorCollector(); @Test public void example() { collector.addError(new Throwable("There is an error in first line")); collector.addError(new Throwable("There is an error in second line")); collector.checkThat(getResults(), not(containsString("here is an error"))); // all lines of code will execute and at the end a combined failure will be logged in. } }
What is @Rule in jUnit?
JUnit provides special kind of handling of tests, Test Case or test suite by using @rule annotation. Using @rule, you can easily add or redefine the behaviour of the test.
There are several built-in rules provided by JUnit API that a tester can use, or even you can write our own rule.
See below line of code, which shows how to use @rule annotation along with Error Collector:
@Rule public ErrorCollector collector= new ErrorCollector();
To understand error collector, let’s create a class and a rule to collect all the errors. You will add all the errors using addError(throwable) here.
See below code which simply creates a rule which is nothing but creating “Error Collector object.” Which is further used to add all the errors in order to report the issue at the end:
ErrorCollectorExample.java
package guru99.junit; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ErrorCollector; public class ErrorCollectorExample { @Rule public ErrorCollector collector = new ErrorCollector(); @Test public void example() { collector.addError(new Throwable("There is an error in first line")); collector.addError(new Throwable("There is an error in second line")); System.out.println("Hello"); try { Assert.assertTrue("A " == "B"); } catch (Throwable t) { collector.addError(t); } System.out.println("World!!!!"); } }
TestRunner.java
Let’s add above test class in a test runner and execute it to collect all errors. See below code:
package guru99.junit; import org.junit.runner.JUnitCore; import org.junit.runner.Result; import org.junit.runner.notification.Failure; public class TestRunner { public static void main(String[] args) { Result result = JUnitCore.runClasses(ErrorCollectorExample.class); for (Failure failure : result.getFailures()) { System.out.println(failure.toString()); } System.out.println("Result=="+result.wasSuccessful()); } }
Output:
See the failure trace which traces all the errors in one place:
Benefits of JUnit ErrorCollector
You can use JUnit assertion for functional or GUI validation e.g.
- assertEquals(String message, Object expected, Object actual) which compare that two objects are equals.
- Similarly, assertTrue(Boolean condition) asserts that a condition is true.
Using assertion, validation test becomes easy. But one major issue is that test execution will stop even if a single assertion fails.
Test continuity and recovery handling is crucial to test automation success. Error Collector is the best way to handle such kind of scenarios.
Summary:
- Junit error collector allows a test to continue even after the first issue is found and test fails at the end
- Error collector collects all error objects and reports it only, after all, the test execution over
- The benefit of adding all errors in an Error Collector is that you can verify all the errors at once
- Error collector simply adds errors using method addError(throwable err) provided by ErrorCollector.java.
В обычном сценарии всякий раз, когда вы обнаруживаете какую-либо ошибку во время выполнения теста, вы останавливаете тест, исправляете ошибку и повторно запускаете тест.
Но у JUnit немного другой подход. С помощью сборщика ошибок JUnit вы все равно можете продолжить выполнение теста даже после обнаружения проблемы или сбоя теста. Сборщик ошибок собирает все объекты ошибок и сообщает об этом только один раз после завершения теста.
При написании тестового сценария вы хотите выполнить все тесты, даже если какая-либо строка кода дает сбой из-за сбоя в сети, сбоя подтверждения или по любой другой причине. В этой ситуации вы все равно можете продолжить выполнение тестового сценария с помощью специальной функции, предоставляемой JUnit, известной как «сборщик ошибок».
Для этого JUnit использует аннотацию @Rule, которая используется для создания объекта сборщика ошибок. После создания объекта для сборщика ошибок вы можете легко добавить все ошибки в объект с помощью метода addError (Throwable error). Как вы знаете, этот Throwable является суперклассом класса Exception и Error в Java. Когда вы добавляете ошибки таким способом, эти ошибки будут зарегистрированы в результате теста JUnit.
Преимущество добавления всех ошибок в сборщик ошибок заключается в том, что вы можете проверить все ошибки одновременно. Кроме того, если сценарий завершится неудачно в середине, он все равно может продолжить его выполнение
Примечание . В случае использования простого блока assert или try / catch использование метода сбора ошибок будет невозможно.
Образец кода
Чтобы понять больше о Error Collector, см. Ниже пример кода, который демонстрирует, как создать объект Error Collector и добавить все ошибки в этом объекте для отслеживания проблемы:
package guru99.junit; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ErrorCollector; public class ErrorCollectorExample { @Rule public ErrorCollector collector = new ErrorCollector(); @Test public void example() { collector.addError(new Throwable("There is an error in first line")); collector.addError(new Throwable("There is an error in second line")); collector.checkThat(getResults(), not(containsString("here is an error"))); // all lines of code will execute and at the end a combined failure will be logged in. } }
Error Collector
Collects raised error during with statement.
Features
In some cases we don’t want to raise an error immediately.
For example, case when return error HTTP response to client
after validating whole HTTP POST data.
This package helps to collect error.
Installation
pip install errorcollector
Usage
MultipleErrorCollector
Let’s say, there are data model which has single property.
Before process this data model, we want to validate property.
Ex:
from yourproduct.exceptions import ConvertError class PostDataModel: def __init__(self, property_a_string: str, property_b_string: str): self._property_a_string = property_a_string self._property_b_string = property_b_string self.list_error = None def validate(self) -> bool: self.stock_convert_error( lambda: self.property_a_int, f"Property string A couldn't be converted to integer. Property string = {self._property_a_string}" ) self.stock_convert_error( lambda: self.property_b_int, f"Property string B couldn't be converted to integer. Property string = {self._property_b_string}" ) return bool(self.list_error) def stock_convert_error(self, method: Callable[[], Any], message: str) -> None: with MultipleErrorCollector(ConvertError, message, self.list_error): return method() @property def property_a_int() -> int: """May raise ValueError""" return int(self._property_a_string) @property def property_b_int() -> int: """May raise ValueError""" return int(self._property_b_string)
When we call method validate()
, even if ValueError
occurs,
the exception is not raised and execution does not stop.
When method()
in method stock_convert_error()
raises ValueError
,
ConvertError
which is set raised ValueError
into __cause__
is set
into property self.list_error
.
We can check details of error after validate.
SingleErrorCollector
This is single version of Error Collector.
This may be useful in case when need to handle
multiple cases and singular cases in an integrated method by polymorphism.
Ex:
from yourproduct.exceptions import ConvertError class PostDataModel: def __init__(self, property_string: str): self._property_string = property_string self.convert_error = None def validate(self) -> bool: self.stock_convert_error( lambda: self.property_int, f"Property string couldn't be converted to integer. Property string = {self._property_string}" ) return self.convert_error is not None def stock_convert_error(self, method: Callable[[], Any], message: str) -> None: error_collector = SingleErrorCollector(ConvertError, message) with error_collector: method() self.convert_error = error_collector.error @property def property_int() -> int: """May raise ValueError""" return int(self._property_string)
This version can be pinned in stack with:collect-errors-0.1.5.0@sha256:f2d3c6cea309fc010a69216eb6882e4d6f09c835145265ef1c307b334da2fb58,1368
Module documentation for 0.1.5.0
- Control
- Control.CollectErrors
- Control.CollectErrors.PreludeInstances
- Control.CollectErrors.Type
- Control.CollectErrors
- Numeric
- Numeric.CollectErrors
- Numeric.CollectErrors.PreludeInstances
- Numeric.CollectErrors.Type
- Numeric.CollectErrors
collect-errors
This package provides an error collecting mechanism. Using CN Double
instead of Double
replaces NaNs and infinities with more informative error descriptions.
API documentation available on the Hackage page.
Table of contents
- 1. Feature highlights
- 1.1. Error collecting
- 1.2. Error investigation
- 1.3. Undecided comparisons
- 1.4. Potential errors
1. Feature highlights
CollectErrors es t
is a monad wrapper around values of type t
which can accommodate (a list of)
(potential) errors of type es
that have (maybe) occurred during the computation
of a value. A value may be missing, leaving only the error(s).
The wrapper CN t
is a special case of CollectErrors es t
with es
= NumErrors
.
1.1. Error collecting
The CN
wrapper propagates instances of Floating
,
allowing us to write expressions with partial
functions (ie functions that fail for some inputs) instead of
branching after each application of such function:
$ stack ghci collect-errors:lib --no-load --ghci-options Numeric.CollectErrors
*Numeric.CollectErrors> a = 1 :: CN Double
*Numeric.CollectErrors> (1/(a-1))+(sqrt (a-2))
{{ERROR: division by 0; ERROR: out of domain: sqrt for negative arg -1.0}}
as opposed to:
*Prelude> a = 1 :: Double
*Prelude> (1/(a-1))+(sqrt (a-2))
NaN
1.2. Error investigation
Dealing with the errors can be moved outside the expression:
*Numeric.CollectErrors> a = 1 :: CN Double
*Numeric.CollectErrors> toEither $ 1/(a-1)
Left {ERROR: division by 0}
*Numeric.CollectErrors> toEither $ 1/a+(sqrt a)
Right 2.0
An alternative way to branch based on errors is provided by the function withErrorOrValue
:
...> a = 2 :: CN Double
...> withErrorOrValue (const 0) id (1/a)
0.5
The CN
wrapper can be forcibly removed as follows:
...> :t unCN (1/a)
... :: Double
...> unCN (1/a)
0.5
...> unCN (1/(a-2))
*** Exception: CollectErrors: {ERROR: division by 0}
1.3. Undecided comparisons
The following examples require the interval arithmetic package aern2-mp and its dependency mixed-types-num:
$ stack ghci aern2-mp:lib --no-load --ghci-options AERN2.MP
*AERN2.MP> import MixedTypesNumPrelude
*AERN2.MP MixedTypesNumPrelude>
Comparisons involving sets (such as intervals) are undecided when the intervals overlap:
...> pi100 = piBallP (prec 100)
...> :t pi100
pi100 :: MPBall
...> pi100
[3.1415926535897932384626433832793333156439620213... ± ~7.8886e-31 ~2^(-100)]
...> 0 < pi100
CertainTrue
...> pi100 == pi100
TrueOrFalse
The values CertainTrue
and TrueOrFalse
are part of the three-valued type Kleenean
provided by
mixed-types-num.
The above equality cannot be decided since pi100
is not a single number but a set of numbers spanning the interval and the comparison operator cannot tell if the two operands sets represent the same number or a different number.
The Prelude Floating
instance for CN
cannot be used reliably with interval arithmetic because the instance relies on true/false comparisons:
...> import qualified Prelude as P
... P> (cn pi100) P./ (cn pi100 - cn pi100)
*** Exception: Failed to decide equality of MPBalls. If you switch to MixedTypesNumPrelude instead of Prelude, comparison of MPBalls returns Kleenean instead of Bool.
Using its Kleenean comparisons, package mixed-types-num provides alternative numerical type classes in which errors are detected and collected correctly when using the CN
wrapper:
...> (cn pi100) / (cn pi100 - cn pi100)
{{POTENTIAL ERROR: division by 0}}
1.4. Potential errors
As we see in the above example, the CN
wrapper supports potential errors that sometimes arise as a consequence of undecided comparisons.
When an error is present (which can be checked using hasError
), the function hasCertainError
can be used to further distinguish cases where the error is certain or potential:
...> import qualified Numeric.CollectErrors as CN
...> CN.hasCertainError $ (cn pi100) / (cn 0)
True
...> CN.hasCertainError $ (cn pi100) / (cn pi100 - cn pi100)
False
Sometimes the potential errors are harmless:
...> sqrt (cn pi100 - cn pi100)
[0.0000000000000006280369834735100420368561502297... ± ~6.2804e-16 ~2^(-50)]{{POTENTIAL ERROR: out of domain: negative sqrt argument}}
Such harmless potential errors can be ignored using clearPotentialErrors
:
...> clearPotentialErrors $ sqrt (cn pi100 - cn pi100)
[0.0000000000000006280369834735100420368561502297... ± ~6.2804e-16 ~2^(-50)]