Liebling, ich habe die Testpyramide auf den Kopf gestellt
FRONTEND
BACKEND
FRONTEND
BACKEND
TESTS
SpringBoot
MariaDB
ElasticSearch
Redis
Such-Service
Image-Service
Tracking-Service
Price-Service
Vue.js
https://de.wikipedia.org/wiki/ISO/IEC_9126
Was nicht getestet ist,
geht kaputt
velocity-tempaltes
messages
model
Contract
getActualEndDateLegacy: Date
getActualEndDateLegacy: Date
Was nicht getestet ist,
geht kaputt
velocity-tempaltes
messages
model
Contract
getActualEndDateLegacy: Date
getActualEndDateLegacy: Date
Was nicht getestet ist,
geht kaputt
Was nicht getestet ist,
geht kaputt
velocity-tempaltes
messages
model
Contract
getActualEndDate: Date
<ul>
#foreach ( $contract in $subs )
<li>
#set($aDate = $dateTool.getDate())
#if ($contract.getActualEndDate())
#set($aDate = $contract.getActualEndDateLegacy())
#end
$contractMessages.message("notify_change_plan_ends", $dateUtils.format($aDate))
</li>
#end
</ul>
notify_change.vm
Was nicht getestet ist,
wird missachtet
Was nicht getestet ist,
wird missachtet
@Test
public void allKeysAreTranslatedInEnglish() {
List<MessageKey> missingEn = newArrayList();
for (MessageKey key : translationLoader.getTranslation()) {
if (key.getValues().get(Locale.GERMAN).hasTranslation() &&
!key.getValues().get(Locale.ENGLISH).hasTranslation()) {
missingEn.add(key);
}
}
String message = "These keys have no English translation in " + listKeys(missingEn);
message += "\n Please provide an English translation";
assertThat(message, missingEn, hasSize(0));
}
java.lang.AssertionError: These keys have no English translation in messages_en.properties:
Account_Api_CollapseAll
Please provide an English translation
Expected: a collection with size <0>
but: collection size was <1>
Was nicht getestet ist,
wird missachtet
> Task :price-stock-service:ktlintMainSourceSetCheck FAILED
C:\Users\..\ServerJob.kt:15 Unused import
C:\Users\..\ServerJob.kt:73 Unexpected indentation (18) (should be 16)
C:\Users\..\ServerJob.kt:74 Unexpected indentation (17) (should be 16)
C:\Users\..\ServerJob.kt:74 Exceeded max line length (160) (cannot be auto-corrected)
> gradle ktlint
Automatisiert
Tests?
private final MoneyParser parserDE = new DefaultMoneyParser();
@Test
public void parseCurrencyDE() throws Exception {
assertThat(parserDE.parseCurrency("20,11"), is(new Money("20.11")));
assertThat(parserDE.parseCurrency("42100,00"), is(new Money("42100.00")));
assertThat(parserDE.parseCurrency("42100"), is(new Money("42100")));
assertThat(parserDE.parseCurrency("42.100"), is(new Money("42100")));
assertThat(parserDE.parseCurrency("42.100,00"), is(new Money("42100.00")));
assertThat(parserDE.parseCurrency("42.100,11"), is(new Money("42100.11")));
assertThat(parserDE.parseCurrency("-42.100,11"), is(new Money("-42100.11")));
}
@Test
public void parseCurrencyDE() throws Exception {
// Preapre
MoneyParser parserDE = new DefaultMoneyParser();
// Execute
Money parsed = parserDE.parseCurrency("20,11");
// Verify
assertThat(parsed, is(new Money("20.11")));
}
Prepare
Execute
Verify
Given
When
Then
@Test
@DisplayName("Parse German currency")
public void parseCurrencyDE() throws Exception {
// Prepare
MoneyParser parserDE = new DefaultMoneyParser();
// Execute
Money parsed = parserDE.parseCurrency("20,11");
// Verify
assertThat(parsed, is(new Money("20.11")));
}
@Test
@DisplayName("Parse German currency")
public void parseCurrencyDE() throws Exception {
// Prepare
MoneyParser parserDE = new DefaultMoneyParser();
// Execute
Money parsed = parserDE.parseCurrency("20,11");
// Verify
assertThat(parsed, is(new Money("20.11")));
}
Kent Beck
Extreme Programming
Test Driven Development
@Test
public void someTest() {
StringBuilder sb = new StringBuilder();
sb.append("test");
sb.append((char) 65);
sb.append((char) 10);
sb.append((char) 13);
sb.append((char) 127);
for (int i = 0; i < 31; i++) {
sb.append((char) i);
}
assertTrue(MessageUtil.foo(sb.toString()).indexOf(10) > -1);
assertTrue(MessageUtil.foo(sb.toString()).indexOf(13) > -1);
assertEquals(MessageUtil.foo(sb.toString()).indexOf(3), -1);
assertEquals(MessageUtil.foo(sb.toString()).indexOf(15), -1);
assertEquals(MessageUtil.foo(sb.toString()).indexOf(28), -1);
}
@Test
public void removeCtrlChars_parameterTest() {
StringBuilder sb = new StringBuilder();
sb.append("test");
sb.append((char) 65);
sb.append((char) 10);
sb.append((char) 13);
sb.append((char) 127);
for (int i = 0; i < 31; i++) {
sb.append((char) i);
}
assertTrue(MessageUtil.removeCtrlChars(sb.toString()).indexOf(10) > -1);
assertTrue(MessageUtil.removeCtrlChars(sb.toString()).indexOf(13) > -1);
assertEquals(MessageUtil.removeCtrlChars(sb.toString()).indexOf(3), -1);
assertEquals(MessageUtil.removeCtrlChars(sb.toString()).indexOf(15), -1);
assertEquals(MessageUtil.removeCtrlChars(sb.toString()).indexOf(28), -1);
}
@Test
public void removeCtrlCharsKeepsNewLine() {
String newLine = String.valueOf((char) 10); // new line (/n)
String cleaned = MessageUtil.removeCtrlChars(newLine);
assertTrue(MessageUtil.removeCtrlChars(newLine).contains(newLine));
}
@Test
public void removeCtrlCharsRemovesShiftIn() {
String shiftIn = String.valueOf((char) 15); // Shift In control (unicode: \u000F)
String cleaned = MessageUtil.removeCtrlChars(shiftIn);
assertFalse(cleaned.contains(shiftIn));
}
@Test
public void removeCtrlCharsRemovesShiftIn() {
String shiftIn = String.valueOf((char) 15); // Shift In control (unicode: \u000F)
String cleaned = MessageUtil.removeCtrlChars(shiftIn);
assertFalse(cleaned.contains(shiftIn));
}
java.lang.AssertionError
at org.junit.Assert.fail(Assert.java:87)
at org.junit.Assert.assertFalse(Assert.java:65)
at de.itscope.mv.service.plcrawler.MessageUtilTest.
removeCtrlCharsRemovesShiftIn(MessageUtilTest.java:39)
@Test
public void removeCtrlCharsRemovesShiftIn() {
String shiftIn = String.valueOf((char) 15); // Shift In control (unicode: \u000F)
String cleaned = MessageUtil.removeCtrlChars(shiftIn);
assertThat(cleaned, is(emptyString()));
}
java.lang.AssertionError:
Expected: is an empty string
but: was ""
Hamcrest
@Test
public void removeCtrlCharsRemovesShiftIn() {
String shiftIn = String.valueOf((char) 15); // Shift In control (unicode: \u000F)
String cleaned = MessageUtil.removeCtrlChars(shiftIn);
assertThat("Shift control symbol should be removed",
cleaned, is(emptyString()));
}
java.lang.AssertionError: Shift control symbol should be removed
Expected: is an empty string
but: was ""
Hamcrest
Ein Software-Tester
läuft in eine Bar und
Jasmine Harpley - ministryoftesting.com
schlendert in die Bar...
gallopiert in die Bar...
rennt in die Bar...
spaziert in die Bar...
Jokin Aspiazu - ministryoftesting.com
Ein Tester
läuft in eine
Bar und
bestellt
-1 Bier.
@Test
public void parseEmpty() {
MoneyParser parserDE = new DefaultMoneyParser();
assertThrows(ParseException.class, () -> parserDE.parseCurrency(""));
}
@Test
public void parseNull() {
MoneyParser parserDE = new DefaultMoneyParser();
assertThrows(ParseException.class, () -> parserDE.parseCurrency(null));
}
@Test
public void parseNull() {
MoneyParser parserDE = new DefaultMoneyParser();
assertThat(parserDE.parseCurrency("4.20"), is(new Money("420")));
}
Automatisiert
langsam
schnell
mehr Integration
mehr Isolation
Automatisiert
API Tests
Integrations Tests
Component Tests
Automatisiert
Service wird hochgefahren
Test von Service-Funktionen, bei dem andere Services gemockt sind
User Ähnlichen Case, der durch
mehrere Codestellen
(Klassen, Funktionen) durchführt
Funktionalität einer Service-Methode im Zusammenspiel mit allen anderen Komponenten sicherstellen
Automatisiert
public class MoneyParserTest
Die 100 schnellsten aus 128 in Modul
max: 0,8 s
4 Fehlgeschlagene Tests
class Product(val name: String)
interface IOrderService {
fun order(product: Product): OrderStatus
}
class Product(val name: String)
interface IOrderService {
fun order(product: Product): OrderStatus
}
class OrderService(private val emailService: IEmailService) : IOrderService {
override fun order(product: Product): OrderStatus {
//...
}
}
class Product(val name: String)
interface IOrderService {
fun order(product: Product): OrderStatus
}
interface IEmailService {
fun sendMail(message: String)
}
enum class OrderStatus { OK, ERROR }
class OrderService(private val emailService: IEmailService) : IOrderService {
override fun order(product: Product): OrderStatus {
//...
}
}
class Product(val name: String)
interface IOrderService {
fun order(product: Product): OrderStatus
}
interface IEmailService {
fun sendMail(message: String)
}
enum class OrderStatus { OK, ERROR }
class OrderService(private val emailService: IEmailService) : IOrderService {
override fun order(product: Product): OrderStatus {
val status: OrderStatus = placeOrder(product)
if (status == OrderStatus.OK) {
emailService.sendMail("You ordered $product")
}
return status
}
private fun placeOrder(product: Product): OrderStatus {
return OrderStatus.OK
}
}
class Product(val name: String)
interface IOrderService {
fun order(product: Product): OrderStatus
}
interface IEmailService {
fun sendMail(message: String)
}
enum class OrderStatus { OK, ERROR }
class OrderService(private val emailService: IEmailService) : IOrderService {
override fun order(product: Product): OrderStatus {
//...
}
}
class Product(val name: String)
interface IOrderService {
fun order(product: Product): OrderStatus
}
interface IEmailService {
fun sendMail(message: String)
}
enum class OrderStatus { OK, ERROR }
class OrderService(private val emailService: IEmailService) : IOrderService {
override fun order(product: Product): OrderStatus {
//...
}
}
class Product(val name: String)
interface IOrderService {
fun order(product: Product): OrderStatus
}
interface IEmailService {
fun sendMail(message: String)
}
enum class OrderStatus { OK, ERROR }
class OrderService(private val emailService: IEmailService) : IOrderService {
override fun order(product: Product): OrderStatus {
//...
}
}
class Product(val name: String)
interface IOrderService {
fun order(product: Product): OrderStatus
}
interface IEmailService {
fun sendMail(message: String)
}
enum class OrderStatus { OK, ERROR }
class OrderService(private val emailService: IEmailService) : IOrderService {
override fun order(product: Product): OrderStatus {
//...
}
}
@Test
fun testOrderService() {
val orderService = OrderService(emailService = ???)
val status = orderService.order(Product("IPad"))
assertEquals(status, OrderStatus.OK)
}
@Test
fun testOrderService() {
val orderService = OrderService(emailService = object : IEmailService {
override fun sendMail(message: String) {
// ignore
}
})
val status = orderService.order(Product("IPad"))
assertEquals(status, OrderStatus.OK)
}
class Product(val name: String)
interface IOrderService {
fun order(product: Product): OrderStatus
}
interface IEmailService {
fun sendMail(message: String)
}
enum class OrderStatus { OK, ERROR }
class OrderService(private val emailService: IEmailService) : IOrderService {
override fun order(product: Product): OrderStatus {
//...
}
}
class Product(val name: String)
interface IOrderService {
fun order(product: Product): OrderStatus
}
interface IEmailService {
fun sendMail(message: String)
}
enum class OrderStatus { OK, ERROR }
class OrderService(private val emailService: IEmailService) : IOrderService {
override fun order(product: Product): OrderStatus {
//...
}
}
class Product(val name: String)
interface IOrderService {
fun order(product: Product): OrderStatus
}
interface IEmailService {
fun sendMail(message: String)
}
enum class OrderStatus { OK, ERROR }
class OrderService(private val emailService: IEmailService) : IOrderService {
override fun order(product: Product): OrderStatus {
//...
}
}
@Test
fun testOrderService() {
val orderService = OrderService(emailService = Mockito.mock(IEmailService::class.java))
val status = orderService.order(Product("IPad"))
assertEquals(status, OrderStatus.OK)
}
@Test
fun testOrderService() {
val orderService = OrderService(...)
val status = orderService.order(Product("IPad"))
assertEquals(status, OrderStatus.OK)
}
@Test
fun testOrderService() {
val emailService = Mockito.mock(IEmailService::class.java)
val orderService = OrderService(emailService)
val status = orderService.order(Product("IPad"))
assertEquals(status, OrderStatus.OK)
}
class Product(val name: String)
interface IOrderService {
fun order(product: Product): OrderStatus
}
interface IEmailService {
fun sendMail(message: String)
}
enum class OrderStatus { OK, ERROR }
class OrderService(private val emailService: IEmailService) : IOrderService {
override fun order(product: Product): OrderStatus {
//...
}
}
class Product(val name: String)
interface IOrderService {
fun order(product: Product): OrderStatus
}
interface IEmailService {
fun sendMail(message: String)
}
enum class OrderStatus { OK, ERROR }
class OrderService(private val emailService: IEmailService) : IOrderService {
override fun order(product: Product): OrderStatus {
//...
}
}
@Test
fun testOrderService() {
val orderService = OrderService(emailService = mockk<IEmailService>()))
val status = orderService.order(Product("IPad"))
assertEquals(status, OrderStatus.OK)
}
mockStatic(SessionUtils.class);
mockStatic(JMX.class);
mockStatic(UI.class);
mockStatic(RequestContexts.class);
mockStatic(Activator.class);
mockStatic(RedgiantPlugin.class);
mockStatic(MetricFactory.class);
mockStatic(RedgiantModelActivator.class);
RedgiantModelActivator mockActivator = mock(RedgiantModelActivator.class);
ApplicationContext mockApplicationContext = mock(ApplicationContext.class);
Environment mockEnvironment = mock(Environment.class);
FrameworkRegistry mockFrameworkRegistry = mock(FrameworkRegistry.class);
DOrderProposalDContainer mockProposalItems = mock(DOrderProposalDContainer.class);
IPartnershipAccountCache mockAccountCache = mock(IPartnershipAccountCache.class);
DCompanySettingsItem mockCompanySettingsItem = mock(DCompanySettingsItem.class);
AbstractProperty mockProperty = mock(AbstractProperty.class);
PushConfiguration mockPushConfiguration = mock(PushConfiguration.class);
AbstractProperty mockTriggerProperty = mock(AbstractProperty.class);
IRequestContext mockRequestContext = mock(IRequestContext.class);
Page mockPage = mock(Page.class);
WebBrowser mockWebBrowser = mock(WebBrowser.class);
Customer mockCustomer = mock(Customer.class);
when(mockCustomer.isShopCustomer()).thenReturn(false);
IAccountCacheService mockAccountCacheService = Mockito.mock(IAccountCacheService.class);
RedgiantPlugin mockRedgiantPlugin = mock(RedgiantPlugin.class);
UserProfile mockProfile = new UserProfile(mock(User.class), mockCustomer);
MOCKS
EVERYWHERE MOCKS
CartOptimizationManagerTest
@Test
fun mockPojoTest() {
val product = mock(JProduct::class.java)
`when`(product.name).thenReturn("mocked")
assertEquals(product.name, "mocked")
}
@Test
fun mockPojoTest() {
val product = mock(JProduct::class.java)
`when`(product.name).thenReturn("mocked")
assertEquals(product.name, "mocked")
product.name = "new"
assertEquals(product.name, "new")
}
fun isWeekday(): Boolean {
return when(LocalDateTime.now().dayOfWeek){
DayOfWeek.SUNDAY,DayOfWeek.SATURDAY -> false
else -> true
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest(LocalDateTime.class)
public class MockTimeTest {
@Test
public void testLocalDateTime() {
mockStatic(LocalDateTime.class);
LocalDateTime now = LocalDateTime.of(2023, 2, 8, 19, 0);
when(LocalDateTime.now()).thenReturn(now);
PowerMockito.stub(PowerMockito.method(LocalDateTime.class, "now")).toReturn(now);
LocalDateTime received = LocalDateTime.now();
assertEquals(now, received);
}
@Test
public void testLocalDateTime(){
try (MockedStatic<?> mocked = mockStatic(LocalDateTime.class)) {
LocalDateTime now = LocalDateTime.of(2023, 2, 8, 19, 0);
mocked.when(LocalDateTime::now).thenReturn(now);
LocalDateTime received = LocalDateTime.now();
assertEquals(now, received);
}
}
A fatal error has been detected by the Java Runtime Environment:
EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ffaf734ac26, pid=32216, tid=22264
fun isWeekdayDefault(now: LocalDateTime = LocalDateTime.now()): Boolean {
return when(now.dayOfWeek){
DayOfWeek.SUNDAY,DayOfWeek.SATURDAY -> true
else -> false
}
}
private long getNumberOfNonStaticFieldsInClass(Class<?> clazz) {
return Arrays.stream(clazz.getDeclaredFields())
.filter(e -> !e.getName().equals("serialVersionUID"))
.filter(e -> !Modifier.isStatic(e.getModifiers()))
.count();
}
@Override
public MarginRule copyEntity() {
MarginRule marginRule = new MarginRule();
marginRule.setSuppliers(suppliers);
marginRule.setProductTypes(productTypes);
marginRule.setManufacturers(manufacturers);
marginRule.setSurcharge(surcharge);
marginRule.setComment(comment);
return marginRule;
}
public void fieldsCheckFor_MarginRule() {
Class<MarginRule> clazz = MarginRule.class;
long numberOfFieldsInClass = getNumberOfNonStaticFieldsInClass(clazz);
assertThat("You added/removed fields in this tracked entity!" +
"Did you take care of #copyEntity()?"
"If so, adjust 'is' value here ->", numberOfFieldsInClass, is(5));
}
public void testCompanyService(){
Log log = getLog(ThreadUtils.class);
CompanyService companyService = new CompanyService();
Company testCompany = new Company("test");
companyService.saveCompany(testCompany);
Company loaded = companyService.loadCompanyByName("test");
log.debug("Loaded Company: " + loaded);
assertEquals(testCompany, loaded);
}
public void testCompanyService(){
Log log = getLog(ThreadUtils.class);
CompanyService companyService = new CompanyService();
Company testCompany = new Company("test");
companyService.saveCompany(testCompany);
Company loaded = companyService.loadCompanyName("test");
if(log.isDebugEnabled()){
log.debug("Loaded Company: " + loaded);
}
assertEquals(testCompany, loaded);
}
@Before
public void setUp() {
TestLogUtil.disableLogForClass(AbstractJacksonWriter.class);
}
public static Log disableLogForClass(Class<?> classWithLogger) {
Log logMock = PowerMockito.mock(Log.class);
Whitebox.setInternalState(classWithLogger, logMock);
return logMock;
}
Log logMock = PowerMockito.mock(Log.class);
Whitebox.setInternalState(classWithLogger, logMock);
return logMock;
}
@Test
public void testResetPasswordDE() {
EmailDef emailDef = new EmailDefMock(EmailTemplate.Password.reset);
ResetPasswordDTO dto = new ResetPasswordDTO("Sehr geehrter Herr Müller",
"mueller@mycomapany.de",
resetPasswordLink);
EmailConfiguration emailConfig = new EmailConfigurationMock(dto, Locale.GERMANY);
EmailConfiguration email = emailDef.createEmail(emailConfig);
String mail = email.getBody();
Approvals.verifyHtml(mail);
}
LinkFactory
ServiceProvider
CompanyService
OrderService
@Test
public void tesRedgiantLinkFactoryNotUsesRedgiantPlugin() {
JavaClasses redgiantLinkFactory = new ClassFileImporter()
.importClasses(LinkFactory.class);
ArchRule rule = ArchRuleDefinition
.theClass(LinkFactory.class)
.should()
.onlyDependOnClassesThat()
.areNotAssignableTo(ServerProvider.class)
.because("It's not allowed to use Services." +
" Instead provide them as parameter.");
rule.check(redgiantLinkFactory);
}
LinkFactory
ServiceProvider
new Label(getContactInformationHtml(contactDetails), ContentMode.HTML);
new Label(getContactInformationHtml(contactDetails), ContentMode.HTML);
new HtmlLabel(getContactInformationHtml(contactDetails), SanitizeMode.NORMAL);
You are here
You are here
SpringApp
Unit-Tests
Integrations-Tests
Selenium-Tests
SpringApp
Unit-Tests
Integrations-Tests
Selenium-Tests
MariaDB
ElasticSearch
Redis
Such-Service
Image-Service
Tracking-Service
integration-test
SpringApp
Unit-Tests
Integrations-Tests
Selenium-Tests
MariaDB
ElasticSearch
Redis
Such-Service
Image-Service
Tracking-Service
integration-test
AbstractIntegrationTest
createTestEnvironment() initCompany() initSession()
Unit-Tests
Integrations-Tests
Selenium-Tests
integration-test
AbstractIntegrationTest
createTestEnvironment() initCompany() initSession()
AllTestsSuite
SofortPaymentTest
EmailPollerTest
erbt
erbt
führt aus
x
CompanyRepository
CompanyService
CompanyServiceAPI
Integration-Test
Company
CompanyService
UserService
CartService
UserService
UserService
CompanyRepository
CompanyService
CompanyServiceAPI
Integration-Test
Company
CompanyService
UserService
CartService
UserService
UserService
CompanyRepository
CompanyService
CompanyServiceAPI
Integration-Test
Company
CompanyService
UserService
CartService
UserService
UserService
CompanyRepository
CompanyService
CompanyServiceAPI
Integartion-Test
Company
CompanyService
CompanyRepository
CompanyService
CompanyServiceAPI
Integartion-Test
Company
CompanyService
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { StudentJpaConfig.class },
loader = AnnotationConfigContextLoader.class),
@PropertySource("h2-datasource.properties")
@Transactional
public class InMemorySampleDBTest {
@Resource
private StudentRepository studentRepository;
@Test
public void givenStudent_whenSave_thenGetOk() {
Student student = new Student(1, "john");
studentRepository.save(student);
Optional<Student> result = studentRepository.findById(1L);
Student found = result.get();
assertEquals("john", found.getName());
}
}
jdbc.driverClassName = org.h2.Driver
jdbc.url = jdbc:h2:mem:myDb
hibernate.dialect = org.hibernate.dialect.H2Dialect
hibernate.hbm2ddl.auto = create
h2-datasource.properties
@BeforeTestClass
fun initMariaDB() {
val dbContainer: JdbcDatabaseContainer<*> = MariaDBContainer(
DockerImageName.parse("mariadb").withTag("10.4.12"))
}
testImplementation("org.testcontainers:testcontainers-bom:1.17.6")
testImplementation('org.testcontainers:mariadb')
@BeforeTestClass
fun initMariaDB() {
val dbContainer: JdbcDatabaseContainer<*> = MariaDBContainer(
DockerImageName.parse("mariadb").withTag("10.4.12"))
.withExposedPorts(3306)
.withUsername("user")
.withPassword("jug")
}
testImplementation("org.testcontainers:testcontainers-bom:1.17.6")
testImplementation('org.testcontainers:mariadb')
INFO o.t.d.DockerClientProviderStrategy : Found Docker environment with local Npipe socket (npipe:////./pipe/docker_engine)
INFO org.testcontainers.DockerClientFactory : Docker host IP address is localhost
INFO org.testcontainers.DockerClientFactory : Connected to docker:
Server Version: 20.10.17
API Version: 1.41
Operating System: Docker Desktop
Total Memory: 25605 MB
INFO 🐳 [testcontainers/ryuk:0.3.4] : Creating container for image: testcontainers/ryuk:0.3.4
INFO 🐳 [testcontainers/ryuk:0.3.4] : Container testcontainers/ryuk:0.3.4 is starting: 7985c01359f682cf674c2e6406ae70e9854ade81577590bf5a37b7bdbfbbbe44
INFO 🐳 [testcontainers/ryuk:0.3.4] : Container testcontainers/ryuk:0.3.4 started in PT1.6241543S
INFO o.t.utility.RyukResourceReaper : Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
INFO org.testcontainers.DockerClientFactory : Checking the system...
INFO org.testcontainers.DockerClientFactory : ✔︎ Docker server version should be at least 1.6.0
INFO 🐳 [mariadb:10.4.12] : Creating container for image: mariadb:10.4.12
INFO 🐳 [mariadb:10.4.12] : Container mariadb:10.4.12 is starting: 0d038e95a9f48ef7169ce0542626a91dd72fc32f8cc5639e9c59314ca7fd7f13
INFO 🐳 [mariadb:10.4.12] : Waiting for database connection to become available at jdbc:mariadb://localhost:63608/test using query 'SELECT 1'
INFO 🐳 [mariadb:10.4.12] : Container is started (JDBC URL: jdbc:mariadb://localhost:63608/test)
INFO 🐳 [mariadb:10.4.12] : Container mariadb:10.4.12 started in PT23.6436749S
CompanyRepository
CompanyService
CompanyServiceAPI
Company
CompanyRepository
CompanyService
CompanyServiceAPI
Integartion-Test
Company
CompanyRepository
CompanyService
CompanyServiceAPI
End-2-End-Test
Company
CompanyRepository
CompanyService
CompanyServiceAPI
Unit-Test
Company
CompanyRepository
CompanyService
CompanyServiceAPI
API-Test
Company
CompanyRepository
CompanyService
CompanyServiceAPI
API-Test
Company
mocked
CompanyRepository
CompanyService
CompanyServiceAPI
Company
Repository-Test
< 10 min
Es darf erst mit dem Produktionscode angefangen werden wenn es einen fehlerhaften Unit Test dazu gibt.
Der Unit Test darf nur soviel erweitert werden, bis er fehlschlägt (oder nicht mehr compiliert).
Der Produktionscode darf nur um soviel erweitert werden, bis der Unit Test grün läuft.
Service A
Service B
Scenario: pathMatches('/storefront/checkout') && methodIs('get')
* def response = read('json/checkoutResult.json')
Test Automation
MadeSimple.
Service A
Feature: Check Cart has correct Supplier Name
Scenario: Cart has correct Supplier Name
Given url host + '/storefront/carts/'
When method GET
Then status 200
* def cart = get response $.supplierCarts.[0]
* def supplierName = get cart $.supplierName
Then match expectedSupplierName == supplierName
cart_test.feature
Service A
Service B
PortalPurchasingBoardTestSf_cartHasSupplierName.feature
Service A
Service A
Service B
FixPriceAutomationPortalOrderTestSf
CartTest.java
Map<String, Object> karateArgs = new HashMap<>();
karateArgs.put("productId", testProduct.getProductIdAsString());
karateArgs.put("supplierId", testSupplier.getSupplierId());
karateArgs.put("qty", 1);
Suite suite = new Suite();
Feature feature = Feature.read(new File("cart_test.feature"));
FeatureRuntime featureRuntime = FeatureRuntime.of(suite, feature, vars);
featureRuntime.run();
assertEquals(featureRuntime.result.getErrorMessages(), 0,
featureRuntime.result.getFailedCount());
Scenario: pathMatches('/storefront/checkout') && methodIs('get')
* def response = read('json/checkoutResult.json')
endpoint.feature
{
"id":"ALFBQU-211112-461",
"orderNumber": "NH1-211110-697",
"customerNumber": "0001",
"orderLimitExceeded": false,
"showCustomerOrderId": true,
"customerOrderIdRequired": false,
"billingAddress": {
"id": "9e4bfcc9-9a84-48de-b0ed-befb051b6108",
"companyName": "ITscope GmbH",
"addition1": "Team Vertrieb Patrick Auth",
"addition2": "",
"street": "Ludwig-Erhard-Allee 202",
"zipCode": "76131",
"city": "Karlsruhe",
"postBox": "",
"iso3country": "FRA",
"phone": "123456",
"fax": "123456",
"email": "",
"clientNumber": "",
"url": "",
"isPrimaryAddress": false,
"isDefaultBillingAddress": true,
"isDefaultDeliveryAddress": true
}
}
checkoutResult.json
Service A
Service A
Service B
mocked
Service A
Service A
Service B
Service A
Service B
import
Service A
Service B
import
data class Stock(
val amount: Int,
val unlimited: Boolean,
val deliveryDate: LocalDate?,
)
Service A
Service B
import
Service A
Service B
import
<dependency>
<groupId>de.itscope.catalog</groupId>
<artifactId>price-stock-api</artifactId>
<version>1.8.3</version>
</dependency>
pom.xml
Weniger zu testen
Service A
Service A
Service B
import
<dependency>
<groupId>de.itscope.catalog</groupId>
<artifactId>price-stock-api</artifactId>
<version>1.8.3</version>
</dependency>
pom.xml
Kotlin
Typescript
Vue
Backend zu Frontend
win win
publishing {
repositories {
maven {
url = Publishing.nexusTargetRepo
isAllowInsecureProtocol = true
credentials {
username = Repository.Nexus.Credentials.username
password = Repository.Nexus.Credentials.password
}
}
}
publications.create("mavenJava", MavenPublication::class) {
from(components["java"])
artifact(sourcesJar.get())
pom {
name.set(artifactId)
developers {
developer {
id.set("its")
name.set("ITscope - Developer")
}
}
}
}
}
build.gradle.kts
You are Here
You are Here
RedGiant
2.562
Unit
335
Integration
801
Selenium
0:27
Unit
0:03
Integration
1:05
Selenium
#107
#108
#109
#110
#111
#112
Wackler
Wackler
Wackler
doTestSetup()
-SideApplicationFrameLayout-GlobalSearchContentView-ContextPanelDetailView
-masterDetailLayout-ProductSmallDetailView-ProductSmallHeaderView
-AddToCartWidget-AddToCartMenuBar
getDriver().findElement(By.className("button"));
getDriver().findElement(By.name("In den Warenkorb"));
getDriver().findElement(By.id("..AddToCartMenuBar.."));
// Open Product Page
IExtendedProduct product = TestProductService.getDefault();
FrontendProductPage productPage = application.openPage().productPage(product);
// Check Product Page Header
assertThat(productPage.getHead().getTitle(), hasText(product.getName()));
assertThat(productPage.getHead().getEan(), hasText(product.getEan()));
Application
ProductPage
Head
Title
resellerApplication.action().createNewCartInPortal("New Cart");
CartRow newCart = portal.getCartTable().getCartByName("New Cart");
newCart.action().addProduct(appleiPadAir);
newCart.action().shareFirstLineItem();
newCart.action().addNegotiationPartner(distributorUser.getUserName());
SearchPage<?> searchPage = openPage().globalSearchNegotiationPage();
SellerResponsePage<?> sellerResponsePage =
searchPage.openFirstSellerResponseFromCompany(getCompany());
sellerResponsePage.getFirstRow().setPrice("1.00");
CartColumnTableRow supplierCard =
newCart.getCartColumn().getCartTable().getFirstSupplierCard();
supplierCard = selectNegotiationPosition(1, newCart, supplierCard);
PriceRequestTest
// #1 (Buyer) create new cart and open it
resellerApplication.action().createNewCartInPortal("New Cart");
// #2 (Buyer) add Product and start negotiation with distri Also
CartRow newCart = portal.getCartTable().getCartByName("New Cart");
newCart.action().addProduct(appleiPadAir);
newCart.action().shareFirstLineItem();
newCart.action().addNegotiationPartner(distributorUser.getUserName());
// #3 (distri) set a price
SearchPage<?> searchPage = openPage().globalSearchNegotiationPage();
SellerResponsePage<?> sellerResponsePage =
searchPage.openFirstSellerResponseFromCompany(getCompany());
sellerResponsePage.getFirstRow().setPrice("1.00");
// #4 Select negotiation price
CartColumnTableRow supplierCard =
newCart.getCartColumn().getCartTable().getFirstSupplierCard();
supplierCard = selectNegotiationPosition(1, newCart, supplierCard);
PriceRequestTest
doTestSetup()
doTestSetup()
getErrorLog()
doTestSetup()
getErrorLog()
1:05
Selenium
0:24
Selenium
1:05
Selenium
0:24
Selenium
Wähle die richtige Etage
Denke zuerst an die Tests
Regelmäßiges
Learn & Adapt
Passes the tests
Reveals intention
Nehmt euch Zeit
fürs Testen
Was nicht getestet ist,
geht kaputt
feed.tobse.eu
Folien
Feedback